Path: blob/master/drivers/isdn/gigaset/asyncdata.c
15112 views
/*1* Common data handling layer for ser_gigaset and usb_gigaset2*3* Copyright (c) 2005 by Tilman Schmidt <[email protected]>,4* Hansjoerg Lipp <[email protected]>,5* Stefan Eilers.6*7* =====================================================================8* This program is free software; you can redistribute it and/or9* modify it under the terms of the GNU General Public License as10* published by the Free Software Foundation; either version 2 of11* the License, or (at your option) any later version.12* =====================================================================13*/1415#include "gigaset.h"16#include <linux/crc-ccitt.h>17#include <linux/bitrev.h>1819/* check if byte must be stuffed/escaped20* I'm not sure which data should be encoded.21* Therefore I will go the hard way and encode every value22* less than 0x20, the flag sequence and the control escape char.23*/24static inline int muststuff(unsigned char c)25{26if (c < PPP_TRANS) return 1;27if (c == PPP_FLAG) return 1;28if (c == PPP_ESCAPE) return 1;29/* other possible candidates: */30/* 0x91: XON with parity set */31/* 0x93: XOFF with parity set */32return 0;33}3435/* == data input =========================================================== */3637/* process a block of received bytes in command mode38* (mstate != MS_LOCKED && (inputstate & INS_command))39* Append received bytes to the command response buffer and forward them40* line by line to the response handler. Exit whenever a mode/state change41* might have occurred.42* Note: Received lines may be terminated by CR, LF, or CR LF, which will be43* removed before passing the line to the response handler.44* Return value:45* number of processed bytes46*/47static unsigned cmd_loop(unsigned numbytes, struct inbuf_t *inbuf)48{49unsigned char *src = inbuf->data + inbuf->head;50struct cardstate *cs = inbuf->cs;51unsigned cbytes = cs->cbytes;52unsigned procbytes = 0;53unsigned char c;5455while (procbytes < numbytes) {56c = *src++;57procbytes++;5859switch (c) {60case '\n':61if (cbytes == 0 && cs->respdata[0] == '\r') {62/* collapse LF with preceding CR */63cs->respdata[0] = 0;64break;65}66/* --v-- fall through --v-- */67case '\r':68/* end of message line, pass to response handler */69if (cbytes >= MAX_RESP_SIZE) {70dev_warn(cs->dev, "response too large (%d)\n",71cbytes);72cbytes = MAX_RESP_SIZE;73}74cs->cbytes = cbytes;75gigaset_dbg_buffer(DEBUG_TRANSCMD, "received response",76cbytes, cs->respdata);77gigaset_handle_modem_response(cs);78cbytes = 0;7980/* store EOL byte for CRLF collapsing */81cs->respdata[0] = c;8283/* cs->dle may have changed */84if (cs->dle && !(inbuf->inputstate & INS_DLE_command))85inbuf->inputstate &= ~INS_command;8687/* return for reevaluating state */88goto exit;8990case DLE_FLAG:91if (inbuf->inputstate & INS_DLE_char) {92/* quoted DLE: clear quote flag */93inbuf->inputstate &= ~INS_DLE_char;94} else if (cs->dle ||95(inbuf->inputstate & INS_DLE_command)) {96/* DLE escape, pass up for handling */97inbuf->inputstate |= INS_DLE_char;98goto exit;99}100/* quoted or not in DLE mode: treat as regular data */101/* --v-- fall through --v-- */102default:103/* append to line buffer if possible */104if (cbytes < MAX_RESP_SIZE)105cs->respdata[cbytes] = c;106cbytes++;107}108}109exit:110cs->cbytes = cbytes;111return procbytes;112}113114/* process a block of received bytes in lock mode115* All received bytes are passed unmodified to the tty i/f.116* Return value:117* number of processed bytes118*/119static unsigned lock_loop(unsigned numbytes, struct inbuf_t *inbuf)120{121unsigned char *src = inbuf->data + inbuf->head;122123gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", numbytes, src);124gigaset_if_receive(inbuf->cs, src, numbytes);125return numbytes;126}127128/* process a block of received bytes in HDLC data mode129* (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 == L2_HDLC)130* Collect HDLC frames, undoing byte stuffing and watching for DLE escapes.131* When a frame is complete, check the FCS and pass valid frames to the LL.132* If DLE is encountered, return immediately to let the caller handle it.133* Return value:134* number of processed bytes135*/136static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)137{138struct cardstate *cs = inbuf->cs;139struct bc_state *bcs = cs->bcs;140int inputstate = bcs->inputstate;141__u16 fcs = bcs->rx_fcs;142struct sk_buff *skb = bcs->rx_skb;143unsigned char *src = inbuf->data + inbuf->head;144unsigned procbytes = 0;145unsigned char c;146147if (inputstate & INS_byte_stuff) {148if (!numbytes)149return 0;150inputstate &= ~INS_byte_stuff;151goto byte_stuff;152}153154while (procbytes < numbytes) {155c = *src++;156procbytes++;157if (c == DLE_FLAG) {158if (inputstate & INS_DLE_char) {159/* quoted DLE: clear quote flag */160inputstate &= ~INS_DLE_char;161} else if (cs->dle || (inputstate & INS_DLE_command)) {162/* DLE escape, pass up for handling */163inputstate |= INS_DLE_char;164break;165}166}167168if (c == PPP_ESCAPE) {169/* byte stuffing indicator: pull in next byte */170if (procbytes >= numbytes) {171/* end of buffer, save for later processing */172inputstate |= INS_byte_stuff;173break;174}175byte_stuff:176c = *src++;177procbytes++;178if (c == DLE_FLAG) {179if (inputstate & INS_DLE_char) {180/* quoted DLE: clear quote flag */181inputstate &= ~INS_DLE_char;182} else if (cs->dle ||183(inputstate & INS_DLE_command)) {184/* DLE escape, pass up for handling */185inputstate |=186INS_DLE_char | INS_byte_stuff;187break;188}189}190c ^= PPP_TRANS;191#ifdef CONFIG_GIGASET_DEBUG192if (!muststuff(c))193gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);194#endif195} else if (c == PPP_FLAG) {196/* end of frame: process content if any */197if (inputstate & INS_have_data) {198gig_dbg(DEBUG_HDLC,199"7e----------------------------");200201/* check and pass received frame */202if (!skb) {203/* skipped frame */204gigaset_isdn_rcv_err(bcs);205} else if (skb->len < 2) {206/* frame too short for FCS */207dev_warn(cs->dev,208"short frame (%d)\n",209skb->len);210gigaset_isdn_rcv_err(bcs);211dev_kfree_skb_any(skb);212} else if (fcs != PPP_GOODFCS) {213/* frame check error */214dev_err(cs->dev,215"Checksum failed, %u bytes corrupted!\n",216skb->len);217gigaset_isdn_rcv_err(bcs);218dev_kfree_skb_any(skb);219} else {220/* good frame */221__skb_trim(skb, skb->len - 2);222gigaset_skb_rcvd(bcs, skb);223}224225/* prepare reception of next frame */226inputstate &= ~INS_have_data;227skb = gigaset_new_rx_skb(bcs);228} else {229/* empty frame (7E 7E) */230#ifdef CONFIG_GIGASET_DEBUG231++bcs->emptycount;232#endif233if (!skb) {234/* skipped (?) */235gigaset_isdn_rcv_err(bcs);236skb = gigaset_new_rx_skb(bcs);237}238}239240fcs = PPP_INITFCS;241continue;242#ifdef CONFIG_GIGASET_DEBUG243} else if (muststuff(c)) {244/* Should not happen. Possible after ZDLE=1<CR><LF>. */245gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);246#endif247}248249/* regular data byte, append to skb */250#ifdef CONFIG_GIGASET_DEBUG251if (!(inputstate & INS_have_data)) {252gig_dbg(DEBUG_HDLC, "7e (%d x) ================",253bcs->emptycount);254bcs->emptycount = 0;255}256#endif257inputstate |= INS_have_data;258if (skb) {259if (skb->len >= bcs->rx_bufsize) {260dev_warn(cs->dev, "received packet too long\n");261dev_kfree_skb_any(skb);262/* skip remainder of packet */263bcs->rx_skb = skb = NULL;264} else {265*__skb_put(skb, 1) = c;266fcs = crc_ccitt_byte(fcs, c);267}268}269}270271bcs->inputstate = inputstate;272bcs->rx_fcs = fcs;273return procbytes;274}275276/* process a block of received bytes in transparent data mode277* (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 != L2_HDLC)278* Invert bytes, undoing byte stuffing and watching for DLE escapes.279* If DLE is encountered, return immediately to let the caller handle it.280* Return value:281* number of processed bytes282*/283static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf)284{285struct cardstate *cs = inbuf->cs;286struct bc_state *bcs = cs->bcs;287int inputstate = bcs->inputstate;288struct sk_buff *skb = bcs->rx_skb;289unsigned char *src = inbuf->data + inbuf->head;290unsigned procbytes = 0;291unsigned char c;292293if (!skb) {294/* skip this block */295gigaset_new_rx_skb(bcs);296return numbytes;297}298299while (procbytes < numbytes && skb->len < bcs->rx_bufsize) {300c = *src++;301procbytes++;302303if (c == DLE_FLAG) {304if (inputstate & INS_DLE_char) {305/* quoted DLE: clear quote flag */306inputstate &= ~INS_DLE_char;307} else if (cs->dle || (inputstate & INS_DLE_command)) {308/* DLE escape, pass up for handling */309inputstate |= INS_DLE_char;310break;311}312}313314/* regular data byte: append to current skb */315inputstate |= INS_have_data;316*__skb_put(skb, 1) = bitrev8(c);317}318319/* pass data up */320if (inputstate & INS_have_data) {321gigaset_skb_rcvd(bcs, skb);322inputstate &= ~INS_have_data;323gigaset_new_rx_skb(bcs);324}325326bcs->inputstate = inputstate;327return procbytes;328}329330/* process DLE escapes331* Called whenever a DLE sequence might be encountered in the input stream.332* Either processes the entire DLE sequence or, if that isn't possible,333* notes the fact that an initial DLE has been received in the INS_DLE_char334* inputstate flag and resumes processing of the sequence on the next call.335*/336static void handle_dle(struct inbuf_t *inbuf)337{338struct cardstate *cs = inbuf->cs;339340if (cs->mstate == MS_LOCKED)341return; /* no DLE processing in lock mode */342343if (!(inbuf->inputstate & INS_DLE_char)) {344/* no DLE pending */345if (inbuf->data[inbuf->head] == DLE_FLAG &&346(cs->dle || inbuf->inputstate & INS_DLE_command)) {347/* start of DLE sequence */348inbuf->head++;349if (inbuf->head == inbuf->tail ||350inbuf->head == RBUFSIZE) {351/* end of buffer, save for later processing */352inbuf->inputstate |= INS_DLE_char;353return;354}355} else {356/* regular data byte */357return;358}359}360361/* consume pending DLE */362inbuf->inputstate &= ~INS_DLE_char;363364switch (inbuf->data[inbuf->head]) {365case 'X': /* begin of event message */366if (inbuf->inputstate & INS_command)367dev_notice(cs->dev,368"received <DLE>X in command mode\n");369inbuf->inputstate |= INS_command | INS_DLE_command;370inbuf->head++; /* byte consumed */371break;372case '.': /* end of event message */373if (!(inbuf->inputstate & INS_DLE_command))374dev_notice(cs->dev,375"received <DLE>. without <DLE>X\n");376inbuf->inputstate &= ~INS_DLE_command;377/* return to data mode if in DLE mode */378if (cs->dle)379inbuf->inputstate &= ~INS_command;380inbuf->head++; /* byte consumed */381break;382case DLE_FLAG: /* DLE in data stream */383/* mark as quoted */384inbuf->inputstate |= INS_DLE_char;385if (!(cs->dle || inbuf->inputstate & INS_DLE_command))386dev_notice(cs->dev,387"received <DLE><DLE> not in DLE mode\n");388break; /* quoted byte left in buffer */389default:390dev_notice(cs->dev, "received <DLE><%02x>\n",391inbuf->data[inbuf->head]);392/* quoted byte left in buffer */393}394}395396/**397* gigaset_m10x_input() - process a block of data received from the device398* @inbuf: received data and device descriptor structure.399*400* Called by hardware module {ser,usb}_gigaset with a block of received401* bytes. Separates the bytes received over the serial data channel into402* user data and command replies (locked/unlocked) according to the403* current state of the interface.404*/405void gigaset_m10x_input(struct inbuf_t *inbuf)406{407struct cardstate *cs = inbuf->cs;408unsigned numbytes, procbytes;409410gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", inbuf->head, inbuf->tail);411412while (inbuf->head != inbuf->tail) {413/* check for DLE escape */414handle_dle(inbuf);415416/* process a contiguous block of bytes */417numbytes = (inbuf->head > inbuf->tail ?418RBUFSIZE : inbuf->tail) - inbuf->head;419gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);420/*421* numbytes may be 0 if handle_dle() ate the last byte.422* This does no harm, *_loop() will just return 0 immediately.423*/424425if (cs->mstate == MS_LOCKED)426procbytes = lock_loop(numbytes, inbuf);427else if (inbuf->inputstate & INS_command)428procbytes = cmd_loop(numbytes, inbuf);429else if (cs->bcs->proto2 == L2_HDLC)430procbytes = hdlc_loop(numbytes, inbuf);431else432procbytes = iraw_loop(numbytes, inbuf);433inbuf->head += procbytes;434435/* check for buffer wraparound */436if (inbuf->head >= RBUFSIZE)437inbuf->head = 0;438439gig_dbg(DEBUG_INTR, "head set to %u", inbuf->head);440}441}442EXPORT_SYMBOL_GPL(gigaset_m10x_input);443444445/* == data output ========================================================== */446447/*448* Encode a data packet into an octet stuffed HDLC frame with FCS,449* opening and closing flags, preserving headroom data.450* parameters:451* skb skb containing original packet (freed upon return)452* Return value:453* pointer to newly allocated skb containing the result frame454* and the original link layer header, NULL on error455*/456static struct sk_buff *HDLC_Encode(struct sk_buff *skb)457{458struct sk_buff *hdlc_skb;459__u16 fcs;460unsigned char c;461unsigned char *cp;462int len;463unsigned int stuf_cnt;464465stuf_cnt = 0;466fcs = PPP_INITFCS;467cp = skb->data;468len = skb->len;469while (len--) {470if (muststuff(*cp))471stuf_cnt++;472fcs = crc_ccitt_byte(fcs, *cp++);473}474fcs ^= 0xffff; /* complement */475476/* size of new buffer: original size + number of stuffing bytes477* + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes478* + room for link layer header479*/480hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + skb->mac_len);481if (!hdlc_skb) {482dev_kfree_skb_any(skb);483return NULL;484}485486/* Copy link layer header into new skb */487skb_reset_mac_header(hdlc_skb);488skb_reserve(hdlc_skb, skb->mac_len);489memcpy(skb_mac_header(hdlc_skb), skb_mac_header(skb), skb->mac_len);490hdlc_skb->mac_len = skb->mac_len;491492/* Add flag sequence in front of everything.. */493*(skb_put(hdlc_skb, 1)) = PPP_FLAG;494495/* Perform byte stuffing while copying data. */496while (skb->len--) {497if (muststuff(*skb->data)) {498*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;499*(skb_put(hdlc_skb, 1)) = (*skb->data++) ^ PPP_TRANS;500} else501*(skb_put(hdlc_skb, 1)) = *skb->data++;502}503504/* Finally add FCS (byte stuffed) and flag sequence */505c = (fcs & 0x00ff); /* least significant byte first */506if (muststuff(c)) {507*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;508c ^= PPP_TRANS;509}510*(skb_put(hdlc_skb, 1)) = c;511512c = ((fcs >> 8) & 0x00ff);513if (muststuff(c)) {514*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;515c ^= PPP_TRANS;516}517*(skb_put(hdlc_skb, 1)) = c;518519*(skb_put(hdlc_skb, 1)) = PPP_FLAG;520521dev_kfree_skb_any(skb);522return hdlc_skb;523}524525/*526* Encode a data packet into an octet stuffed raw bit inverted frame,527* preserving headroom data.528* parameters:529* skb skb containing original packet (freed upon return)530* Return value:531* pointer to newly allocated skb containing the result frame532* and the original link layer header, NULL on error533*/534static struct sk_buff *iraw_encode(struct sk_buff *skb)535{536struct sk_buff *iraw_skb;537unsigned char c;538unsigned char *cp;539int len;540541/* size of new buffer (worst case = every byte must be stuffed):542* 2 * original size + room for link layer header543*/544iraw_skb = dev_alloc_skb(2*skb->len + skb->mac_len);545if (!iraw_skb) {546dev_kfree_skb_any(skb);547return NULL;548}549550/* copy link layer header into new skb */551skb_reset_mac_header(iraw_skb);552skb_reserve(iraw_skb, skb->mac_len);553memcpy(skb_mac_header(iraw_skb), skb_mac_header(skb), skb->mac_len);554iraw_skb->mac_len = skb->mac_len;555556/* copy and stuff data */557cp = skb->data;558len = skb->len;559while (len--) {560c = bitrev8(*cp++);561if (c == DLE_FLAG)562*(skb_put(iraw_skb, 1)) = c;563*(skb_put(iraw_skb, 1)) = c;564}565dev_kfree_skb_any(skb);566return iraw_skb;567}568569/**570* gigaset_m10x_send_skb() - queue an skb for sending571* @bcs: B channel descriptor structure.572* @skb: data to send.573*574* Called by LL to encode and queue an skb for sending, and start575* transmission if necessary.576* Once the payload data has been transmitted completely, gigaset_skb_sent()577* will be called with the skb's link layer header preserved.578*579* Return value:580* number of bytes accepted for sending (skb->len) if ok,581* error code < 0 (eg. -ENOMEM) on error582*/583int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)584{585struct cardstate *cs = bcs->cs;586unsigned len = skb->len;587unsigned long flags;588589if (bcs->proto2 == L2_HDLC)590skb = HDLC_Encode(skb);591else592skb = iraw_encode(skb);593if (!skb) {594dev_err(cs->dev,595"unable to allocate memory for encoding!\n");596return -ENOMEM;597}598599skb_queue_tail(&bcs->squeue, skb);600spin_lock_irqsave(&cs->lock, flags);601if (cs->connected)602tasklet_schedule(&cs->write_tasklet);603spin_unlock_irqrestore(&cs->lock, flags);604605return len; /* ok so far */606}607EXPORT_SYMBOL_GPL(gigaset_m10x_send_skb);608609610