Re: tty line discipline

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Thank you Arjan for the remarks. I want to establish communication
between a host and a controller. The controller is connected to the
serial port ttyS0. I want to be able to specify how the driver
receives and sends the data.  I thought this would require a line
discipline because a driver for ttyS0 already exists. Please correct
me if I was wrong.

Previously, I used to initialize the module and register the line
discipline to N_MOUSE. Then I would run a C-file from user space with
the code:

i=N_MOUSE;
ioctl(fd,TIOCSETD,&i);

This would assign the line discipline to ttyS0 successfully. Now, I
want to establish communication directly from kernel space.

As can be seen from the attached source file, the only fields from the
tty_ldisc structure that I'm using are receive_buf (tl_tty_receive)
and write_wakeup(tl_tty_wakeup).

If the problem is still unclear please tell me.


On 10/19/05, Arjan van de Ven <[email protected]> wrote:
> On Wed, 2005-10-19 at 13:55 +0200, Rabih ElMasri wrote:
> > hi,
> >
> > I am trying to assign a new line discipline to ttyS0 from within the
> > kernel-space.
>
> why? You don't describe what you want to solve, only how you want to do
> it... it might be entirely the wrong solution for a simple problem..
>
>
> >  During the initialization of my module I do the
> > following:
>
> you forgot to attach the full source of your module or point to URL with
> that. That is basically rule nr 1 when posting about problems with
> out-of-kernel modules; how else are people supposed to help you?
>
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>
#include <linux/fs.h>
#include <linux/syscalls.h>	
#include <linux/ioctl.h>

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>		//for module_exit/init
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/poll.h>
#include <linux/slab.h> 	//for kmalloc

#include "host.h"

typedef struct PacketInfo_tag
   {
     UINT16 uiWritePtr;
     UINT16 uiLen;
     UINT8* pucPacket;
   } T_PacketInfo;

static enum
{
	Starting_delimiter,
	After_delimiter
} pktStage =Starting_delimiter;


static struct tty_struct *pstLocaltty;
static T_PacketInfo stSendFrame;


MODULE_AUTHOR("Rabih ElMasri");
MODULE_LICENSE("GPL");


void SendFrame(unsigned char *Frame,unsigned int length)
{
	
    len = pstLocaltty->driver->write(pstLocaltty, Frame, length);
    	if (len < Length)
    	{
    	
//the rest will be sent after tl_tty_wakeup is called from lowlevel device driver
    	   printk("Send not complete\n");
    	   stSendFrame.pucPacket=Ptr;
    	   stSendFrame.uiWritePtr=len;
    	   stSendFrame.uiLen=Length;
    	}
    	else
    	{
    	     printk("Send complete\n");
		}
	
		
	return;
}
EXPORT_SYMBOL(SendFrame);


/* tl_tty_open
 * 
 *     Called when line discipline changed to ours.
 *
 * Arguments:
 *     tty    pointer to tty info structure
 * Return Value:    
 *     0 if success, otherwise error code
 */
static int tl_tty_open(struct tty_struct *tty)
{
   printk("tl_tty_open\n");   

   //pstLocaltty = tty;
   
   /* Flush any pending characters in the driver and line discipline. */
   /* FIXME: why is this needed. Note don't use ldisc_ref here as the
      open path is before the ldisc is referencable */
   if (tty->ldisc.flush_buffer)
	   tty->ldisc.flush_buffer(tty);

   if (tty->driver->flush_buffer)
	   tty->driver->flush_buffer(tty);

   
   return 0;
}

/* tl_tty_close()
 *
 *    Called when the line discipline is changed to something
 *    else, the tty is closed, or the tty detects a hangup.
 */
static void tl_tty_close(struct tty_struct *tty)
{
   printk("tl_tty_close\n");   
   pstLocaltty=NULL;
}

/* tl_tty_ioctl()
 *
 *    Process IOCTL system call for the tty device.
 *
 * Arguments:
 *
 *    tty        pointer to tty instance data
 *    file       pointer to open file object for device
 *    cmd        IOCTL command code
 *    arg        argument for IOCTL call (cmd dependent)
 *
 * Return Value:    Command dependent
 */
static int tl_tty_ioctl(struct tty_struct *tty, struct file * file,unsigned int cmd, unsigned long arg)
{
   int err = 0;

   printk("tl_tty_ioctl\n");   

   switch (cmd) {
   default:
	   err = n_tty_ioctl(tty, file, cmd, arg);
	   break;
   };

   return err;
}


/* ()
 *
 *    Callback for transmit wakeup. Called when low level
 *    device driver can accept more send data.
 *
 * Arguments:        tty    pointer to associated tty instance data
 * Return Value:    None
 */
static void tl_tty_wakeup(struct tty_struct *tty)
{
   unsigned int uilen;
   unsigned int uiLenRemain = stSendFrame.uiLen-stSendFrame.uiWritePtr;
   printk("tl_tty_wakeup\n");   

   clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);

   if (NULL!=stSendFrame.pucPacket)
   {
      uilen = pstLocaltty->driver->write(pstLocaltty, stSendFrame.pucPacket+stSendFrame.uiWritePtr,uiLenRemain);
      if (uilen < uiLenRemain)
      {
			
			stSendFrame.uiWritePtr=uiLenRemain-uilen;
      }
      else
      {
			stSendFrame.pucPacket=NULL;
			stSendFrame.uiWritePtr=0;
			stSendFrame.uiLen=0;
	  }
   }


}

/* tl_tty_room()
 * 
 *    Callback function from tty driver. Return the amount of 
 *    space left in the receiver's buffer to decide if remote
 *    transmitter is to be throttled.
 *
 * Arguments:        tty    pointer to associated tty instance data
 * Return Value:    number of bytes left in receive buffer
 */
static int tl_tty_room (struct tty_struct *tty)
{
   printk("tl_tty_room\n");   

   return 65536;
}



/* tl_tty_receive()
 * 
 *     Called by tty low level driver when receive data is
 *     available.
 *     
 * Arguments:  tty          pointer to tty instance data
 *             data         pointer to received data
 *             flags        pointer to flags for data
 *             count        count of received data in bytes
 *     
 * Return Value:    None
 */
static void tl_tty_receive(struct tty_struct *tty, const __u8 *data, char *flags, int count)
{
   UINT16 uiReadPtr= 0;
   UINT8  Byte;
   UINT8* pack;
   int	  ind;

   printk("tl_tty_receive\n");   
   
   if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver->unthrottle)
	tty->driver->unthrottle(tty);
   // Parse the data
   
   pack= (UINT8*)kmalloc(sizeof(UINT8)*1000,GFP_KERNEL);
   pktStage=Starting_delimiter;

   while ( (uiReadPtr<count))
   {
   		
		memcpy (&Byte, data+uiReadPtr, 1);
   		uiReadPtr++;
   	  	
		//printk("%02X, %d\n ",Byte,ind);
    			
   		switch(pktStage)
		{
			case	Starting_delimiter:
	
				if(Byte==0xf0)
				{
					pack[0]=Byte;
					ind=1;
					pktStage=After_delimiter;
				}
				break;
			
			case	After_delimiter:
				if(Byte!=0xf0)	
				{
					pack[ind]=Byte;
					ind++;
				}
				else if(Byte==0xf0)
				{
					pack[ind]=Byte;
					ind=0;
					pktStage=Starting_delimiter;
					Incoming(pack);
				}
				break;
		}
   }
 }



/*
 * We don't provide read/write/poll interface for user space.
 */
static ssize_t tl_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr)
{
	return 0;
}
static ssize_t tl_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
{
	return 0;
}
static unsigned int tl_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
{
	return 0;
}

int init_uart(char *dev)
{
    
    //int i;
    //int err;
    struct termios newtio;//,oldtio;
	struct file 	*fp; 			//refer to linux/fs.h for the struct
	
    fp = filp_open(dev, O_RDWR | O_NOCTTY,0); //refer to fs/open.c for filp_open()
    //filp_open calls tty_open which stores a pointer to the tty_struct in the private_data field
    
    if (IS_ERR(fp))
    {
	    printk("Can't open serial port\n");
	    return -1;
    }

//latest    
    /*
    if((err=n_tty_ioctl((struct tty_struct *)(fp->private_data), fp,TCGETA,&oldtio))<0) //prototype in tty.h, found in tty_ioctl.c
    {
    	printk("Can't get the old termios. Error:%d,%d,%d \n",err,EBADF,ENOTTY);
        //return -1;
    }
    */
    
    newtio.c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD;
    newtio.c_iflag = IGNPAR;
    newtio.c_oflag = 0;

    /* set input mode (non-canonical, no echo,...) */
    newtio.c_lflag = 0;

    newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
    newtio.c_cc[VMIN]     = 1;   /* blocking read until 1 chars received */

    pstLocaltty=(struct tty_struct *)(fp->private_data);
    
    //setting the new termios of the tty_struct
    pstLocaltty->termios=&newtio; //refer to struct tty_struct in tty.h
      
//latest     
    //setting the new termios in the tty_struct   
 /*
    if((err=n_tty_ioctl((struct tty_struct *)(fp->private_data), fp,TCSETA,&newtio))<0) //prototype in tty.h, found in tty_ioctl.c
    {
    	printk("Can't set the new termios. Error: %d,%d,%d,%d,%d,%d \n",err,EBADF,ENOTTY,EINTR,EINVAL,EIO);
        return -1;
    }
    
    //assigning the new line discipline
    //the important bypass (N_MOUSE is unused in Linux, so we use it here)
    //this will call tl_tty_open
    
    i = N_MOUSE;    
    //if((err=fp->f_op->ioctl(fp->f_dentry->d_inode,fp, TIOCSETD,&i))<0) //refer to tty_ioctl in tty_io.c (usr/src/linux/drivers/char/)
    if(err=((struct tty_struct *)(fp->private_data))->driver->ioctl((struct inode *)fp->f_dentry->d_inode,fp, TIOCSETD, &i)<0) 
    {
	    printk("Can't set line discipline. Error: %d,%d,%d,%d,%d,%d \n",err,EBADF,ENOTTY,EINTR,EINVAL,EIO);
	    return -1;
    }
   */ 
    return 1;
}



static int __init tl_init(void)
{
	static struct tty_ldisc tl_ldisc;
	int err;
	int n;

	/* Register the tty discipline */
	printk(": tl_init\n");

	memset(&tl_ldisc, 0, sizeof (tl_ldisc));
	tl_ldisc.magic       = TTY_LDISC_MAGIC;
	tl_ldisc.name        = "n_tl";
	tl_ldisc.open        = tl_tty_open;
	tl_ldisc.close       = tl_tty_close;
	tl_ldisc.read        = tl_tty_read;
	tl_ldisc.write       = tl_tty_write;
	tl_ldisc.ioctl       = tl_tty_ioctl;
	tl_ldisc.poll        = tl_tty_poll;
	tl_ldisc.receive_room= tl_tty_room;
	tl_ldisc.receive_buf = tl_tty_receive;
	tl_ldisc.write_wakeup= tl_tty_wakeup;
	tl_ldisc.owner       = THIS_MODULE;

	if ((err = tty_register_ldisc(N_MOUSE, &tl_ldisc))) {
		printk(": HCI line discipline registration failed. (%d)\n", err);
		return err;
	}


	//initializing the serial interface
	n = init_uart("/dev/ttyS0");
	if (n < 0)
	{
	   printk("\nCan't initialize device\n");
	   return -1;	//any value other than zero indicates an error
   	}
   	
   	//setting the new line discipline
   	
 
 	pstLocaltty->ldisc=tl_ldisc;	//refer to struct tty_struct in tty.h
	pstLocaltty->ldisc.refcount = 0;
	(pstLocaltty->ldisc.open)(pstLocaltty);

	return 0;
}

static void __exit tl_exit(void)
{
	int err;
	
	//fp->f_op->release(fp->f_dentry->d_inode,fp);
	//fp=NULL;

	(pstLocaltty->ldisc.close)(pstLocaltty);
	pstLocaltty=NULL;

	/* Release tty registration of line discipline */
	if ((err = tty_register_ldisc(N_MOUSE, NULL)))
		printk(": Can't unregister HCI line discipline (%d)\n", err);
	
}


module_init(tl_init);
module_exit(tl_exit);


[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux