2007-02-12 11:52:34 +03:00
/* 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>
2008-02-06 12:38:30 +03:00
# include <linux/completion.h>
2007-02-12 11:52:34 +03:00
/* 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 ;
2008-02-06 12:38:30 +03:00
struct completion dead_cmp ;
2007-02-12 11:52:34 +03:00
} ;
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 ;
2008-04-30 11:54:13 +04:00
int sent = - EOPNOTSUPP ;
2007-02-12 11:52:34 +03:00
2015-12-15 20:11:29 +03:00
WARN_ON ( ! tty | | ! tty - > ops | | ! skb ) ;
2007-02-12 11:52:34 +03:00
if ( ! skb - > len ) {
dev_kfree_skb_any ( skb ) ;
bcs - > tx_skb = NULL ;
return - EINVAL ;
}
set_bit ( TTY_DO_WRITE_WAKEUP , & tty - > flags ) ;
2008-04-30 11:54:13 +04:00
if ( tty - > ops - > write )
sent = tty - > ops - > write ( tty , skb - > data , skb - > len ) ;
2007-02-12 11:52:34 +03:00
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 ;
2015-12-15 20:11:29 +03:00
WARN_ON ( ! tty | | ! tty - > ops ) ;
2007-02-12 11:52:34 +03:00
cb = cs - > cmdbuf ;
if ( ! cb )
return 0 ; /* nothing to do */
if ( cb - > len ) {
set_bit ( TTY_DO_WRITE_WAKEUP , & tty - > flags ) ;
2008-04-30 11:54:13 +04:00
sent = tty - > ops - > write ( tty , cb - > buf + cb - > offset , cb - > len ) ;
2007-02-12 11:52:34 +03:00
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 ;
2009-10-25 12:30:27 +03:00
struct sk_buff * nextskb ;
2007-02-12 11:52:34 +03:00
int sent = 0 ;
2009-10-25 12:30:27 +03:00
if ( ! cs ) {
gig_dbg ( DEBUG_OUTPUT , " %s: no cardstate " , __func__ ) ;
return ;
}
bcs = cs - > bcs ;
if ( ! bcs ) {
2007-02-12 11:52:34 +03:00
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 */
2009-10-25 12:30:27 +03:00
nextskb = skb_dequeue ( & bcs - > squeue ) ;
if ( ! nextskb )
2007-02-12 11:52:34 +03:00
/* no skb either, nothing to do */
return ;
2009-10-25 12:30:27 +03:00
bcs - > tx_skb = nextskb ;
2007-02-12 11:52:34 +03:00
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
*/
2010-07-05 18:18:43 +04:00
static int gigaset_write_cmd ( struct cardstate * cs , struct cmdbuf_t * cb )
2007-02-12 11:52:34 +03:00
{
unsigned long flags ;
2008-02-06 12:38:28 +03:00
gigaset_dbg_buffer ( cs - > mstate ! = MS_LOCKED ?
2012-02-20 07:52:38 +04:00
DEBUG_TRANSCMD : DEBUG_LOCKCMD ,
2010-07-05 18:18:43 +04:00
" CMD Transmit " , cb - > len , cb - > buf ) ;
2007-02-12 11:52:34 +03:00
spin_lock_irqsave ( & cs - > cmdlock , flags ) ;
cb - > prev = cs - > lastcmdbuf ;
if ( cs - > lastcmdbuf )
cs - > lastcmdbuf - > next = cb ;
else {
cs - > cmdbuf = cb ;
2010-07-05 18:18:43 +04:00
cs - > curlen = cb - > len ;
2007-02-12 11:52:34 +03:00
}
2010-07-05 18:18:43 +04:00
cs - > cmdbytes + = cb - > len ;
2007-02-12 11:52:34 +03:00
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 ) ;
2010-07-05 18:18:43 +04:00
return cb - > len ;
2007-02-12 11:52:34 +03:00
}
/*
* 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 ;
2012-04-25 17:02:20 +04:00
return 0 ;
2007-02-12 11:52:34 +03:00
}
/*
* Free B channel structure
* Called by " gigaset_freebcs " in common . c
*/
2012-04-25 17:02:20 +04:00
static void gigaset_freebcshw ( struct bc_state * bcs )
2007-02-12 11:52:34 +03:00
{
/* unused */
}
/*
* Reinitialize B channel structure
* This is called by " bcs_reinit " in common . c
*/
static void gigaset_reinitbcshw ( struct bc_state * bcs )
{
/* nothing to do for M10x */
}
/*
* Free hardware specific device data
* This will be called by " gigaset_freecs " in common . c
*/
static void gigaset_freecshw ( struct cardstate * cs )
{
tasklet_kill ( & cs - > write_tasklet ) ;
if ( ! cs - > hw . ser )
return ;
platform_device_unregister ( & cs - > hw . ser - > dev ) ;
}
static void gigaset_device_release ( struct device * dev )
{
2016-02-18 23:29:08 +03:00
kfree ( container_of ( dev , struct ser_cardstate , dev . dev ) ) ;
2007-02-12 11:52:34 +03:00
}
/*
* Set up hardware specific device data
* This is called by " gigaset_initcs " in common . c
*/
static int gigaset_initcshw ( struct cardstate * cs )
{
int rc ;
2009-10-25 12:30:27 +03:00
struct ser_cardstate * scs ;
2007-02-12 11:52:34 +03:00
2009-10-25 12:30:27 +03:00
scs = kzalloc ( sizeof ( struct ser_cardstate ) , GFP_KERNEL ) ;
if ( ! scs ) {
2008-12-26 12:21:29 +03:00
pr_err ( " out of memory \n " ) ;
2012-04-25 17:02:20 +04:00
return - ENOMEM ;
2007-02-12 11:52:34 +03:00
}
2009-10-25 12:30:27 +03:00
cs - > hw . ser = scs ;
2007-02-12 11:52:34 +03:00
cs - > hw . ser - > dev . name = GIGASET_MODULENAME ;
cs - > hw . ser - > dev . id = cs - > minor_index ;
cs - > hw . ser - > dev . dev . release = gigaset_device_release ;
2009-10-25 12:30:27 +03:00
rc = platform_device_register ( & cs - > hw . ser - > dev ) ;
if ( rc ! = 0 ) {
2008-12-26 12:21:29 +03:00
pr_err ( " error %d registering platform device \n " , rc ) ;
2007-02-12 11:52:34 +03:00
kfree ( cs - > hw . ser ) ;
cs - > hw . ser = NULL ;
2012-04-25 17:02:20 +04:00
return rc ;
2007-02-12 11:52:34 +03:00
}
tasklet_init ( & cs - > write_tasklet ,
2009-11-19 12:55:53 +03:00
gigaset_modem_fill , ( unsigned long ) cs ) ;
2012-04-25 17:02:20 +04:00
return 0 ;
2007-02-12 11:52:34 +03:00
}
/*
* 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
*/
2009-10-25 12:30:27 +03:00
static int gigaset_set_modem_ctrl ( struct cardstate * cs , unsigned old_state ,
unsigned new_state )
2007-02-12 11:52:34 +03:00
{
struct tty_struct * tty = cs - > hw . ser - > tty ;
unsigned int set , clear ;
2015-12-15 20:11:29 +03:00
WARN_ON ( ! tty | | ! tty - > ops ) ;
/* tiocmset is an optional tty driver method */
if ( ! tty - > ops - > tiocmset )
2008-04-30 11:54:13 +04:00
return - EINVAL ;
2007-02-12 11:52:34 +03:00
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 ) ;
2011-02-14 19:26:50 +03:00
return tty - > ops - > tiocmset ( tty , set , clear ) ;
2007-02-12 11:52:34 +03:00
}
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 ;
}
2007-03-29 12:20:34 +04:00
static const struct gigaset_ops ops = {
2007-02-12 11:52:34 +03:00
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 ) )
2008-02-06 12:38:30 +03:00
complete ( & cs - > hw . ser - > dead_cmp ) ;
2007-02-12 11:52:34 +03:00
}
/*
* 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 ;
2012-04-25 17:02:20 +04:00
int rc ;
2007-02-12 11:52:34 +03:00
gig_dbg ( DEBUG_INIT , " Starting HLL for Gigaset M101 " ) ;
2008-12-26 12:21:29 +03:00
pr_info ( DRIVER_DESC " \n " ) ;
2007-02-12 11:52:34 +03:00
if ( ! driver ) {
2008-12-26 12:21:29 +03:00
pr_err ( " %s: no driver structure \n " , __func__ ) ;
2007-02-12 11:52:34 +03:00
return - ENODEV ;
}
tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-11-01 22:38:34 +03:00
/* allocate memory for our device state and initialize it */
2009-10-25 12:30:27 +03:00
cs = gigaset_initcs ( driver , 1 , 1 , 0 , cidmode , GIGASET_MODULENAME ) ;
2012-04-25 17:02:20 +04:00
if ( ! cs ) {
rc = - ENODEV ;
2007-02-12 11:52:34 +03:00
goto error ;
2012-04-25 17:02:20 +04:00
}
2007-02-12 11:52:34 +03:00
cs - > dev = & cs - > hw . ser - > dev . dev ;
cs - > hw . ser - > tty = tty ;
atomic_set ( & cs - > hw . ser - > refcnt , 1 ) ;
2008-02-06 12:38:30 +03:00
init_completion ( & cs - > hw . ser - > dead_cmp ) ;
2007-02-12 11:52:34 +03:00
tty - > disc_data = cs ;
2015-07-14 01:37:13 +03:00
/* Set the amount of data we're willing to receive per call
* from the hardware driver to half of the input buffer size
* to leave some reserve .
* Note : We don ' t do flow control towards the hardware driver .
* If more data is received than will fit into the input buffer ,
* it will be dropped and an error will be logged . This should
* never happen as the device is slow and the buffer size ample .
*/
tty - > receive_room = RBUFSIZE / 2 ;
2007-02-12 11:52:34 +03:00
/* 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 )
2008-02-06 12:38:28 +03:00
cs - > mstate = MS_LOCKED ;
2012-04-25 17:02:20 +04:00
rc = gigaset_start ( cs ) ;
if ( rc < 0 ) {
2007-02-12 11:52:34 +03:00
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 ) ;
2012-04-25 17:02:20 +04:00
return rc ;
2007-02-12 11:52:34 +03:00
}
/*
* 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 )
2008-12-26 12:21:29 +03:00
pr_err ( " %s: no hw cardstate \n " , __func__ ) ;
2007-02-12 11:52:34 +03:00
else {
/* wait for running methods to finish */
if ( ! atomic_dec_and_test ( & cs - > hw . ser - > refcnt ) )
2008-02-06 12:38:30 +03:00
wait_for_completion ( & cs - > hw . ser - > dead_cmp ) ;
2007-02-12 11:52:34 +03:00
}
/* 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 ;
}
/*
* 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 ) {
2008-10-13 13:44:43 +04:00
case FIONREAD :
/* unused, always return zero */
val = 0 ;
rc = put_user ( val , p ) ;
2007-02-12 11:52:34 +03:00
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 ;
}
2008-10-13 13:44:43 +04:00
/* Pass through */
2007-02-12 11:52:34 +03:00
default :
2008-10-13 13:44:43 +04:00
/* pass through to underlying serial device */
rc = n_tty_ioctl_helper ( tty , file , cmd , arg ) ;
break ;
2007-02-12 11:52:34 +03:00
}
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
*/
2011-06-04 01:33:24 +04:00
static void
2007-02-12 11:52:34 +03:00
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 )
2011-06-04 01:33:24 +04:00
return ;
2009-10-25 12:30:27 +03:00
inbuf = cs - > inbuf ;
if ( ! inbuf ) {
2007-02-12 11:52:34 +03:00
dev_err ( cs - > dev , " %s: no inbuf \n " , __func__ ) ;
cs_put ( cs ) ;
2011-06-04 01:33:24 +04:00
return ;
2007-02-12 11:52:34 +03:00
}
2008-02-06 12:38:28 +03:00
tail = inbuf - > tail ;
head = inbuf - > head ;
2007-02-12 11:52:34 +03:00
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 ) ;
2008-02-06 12:38:28 +03:00
inbuf - > tail = tail ;
2007-02-12 11:52:34 +03:00
/* 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 ) ;
}
2008-07-17 00:53:12 +04:00
static struct tty_ldisc_ops gigaset_ldisc = {
2007-02-12 11:52:34 +03:00
. owner = THIS_MODULE ,
. magic = TTY_LDISC_MAGIC ,
. name = " ser_gigaset " ,
. open = gigaset_tty_open ,
. close = gigaset_tty_close ,
. hangup = gigaset_tty_hangup ,
. 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__ ) ;
2009-10-25 12:30:27 +03:00
rc = platform_driver_register ( & device_driver ) ;
if ( rc ! = 0 ) {
2008-12-26 12:21:29 +03:00
pr_err ( " error %d registering platform driver \n " , rc ) ;
2007-02-12 11:52:34 +03:00
return rc ;
}
tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-11-01 22:38:34 +03:00
/* allocate memory for our driver state and initialize it */
2009-10-25 12:30:27 +03:00
driver = gigaset_initdriver ( GIGASET_MINOR , GIGASET_MINORS ,
2012-02-20 07:52:38 +04:00
GIGASET_MODULENAME , GIGASET_DEVNAME ,
& ops , THIS_MODULE ) ;
2009-10-25 12:30:27 +03:00
if ( ! driver )
2007-02-12 11:52:34 +03:00
goto error ;
2009-10-25 12:30:27 +03:00
rc = tty_register_ldisc ( N_GIGASET_M101 , & gigaset_ldisc ) ;
if ( rc ! = 0 ) {
2008-12-26 12:21:29 +03:00
pr_err ( " error %d registering line discipline \n " , rc ) ;
2007-02-12 11:52:34 +03:00
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 ;
}
2009-10-25 12:30:27 +03:00
rc = tty_unregister_ldisc ( N_GIGASET_M101 ) ;
if ( rc ! = 0 )
2008-12-26 12:21:29 +03:00
pr_err ( " error %d unregistering line discipline \n " , rc ) ;
2007-02-12 11:52:34 +03:00
platform_driver_unregister ( & device_driver ) ;
}
module_init ( ser_gigaset_init ) ;
module_exit ( ser_gigaset_exit ) ;