LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Software > Linux - Kernel
User Name
Password
Linux - Kernel This forum is for all discussion relating to the Linux kernel.

Notices


Reply
  Search this Thread
Old 05-12-2013, 03:06 AM   #1
diwsdiwa
LQ Newbie
 
Registered: May 2013
Location: Pune
Posts: 2

Rep: Reputation: Disabled
Kernel panics : trying to write / read on tiny tty driver


I'm a beginner to the Linux programming and trying my hands on some device driver examples while practising. The below code (a trimmed down version of tiny_tty.c from ldd3 book) loads perfectly using insmod and I'm able to see it in /proc/tty/drivers , /proc/modules and device nodes are getting created under /dev. When I try to write to device file like echo "abcd" > /dev/ttyms0 or read like cat /dev/ttyms0, the kernel panics with a call trace on the screen. I'm on kernel 3.5.0 under Ubuntu. Any help is very much appreciated.
I tried initializing the timer in open function and below is the call trace for "kernel panic - not syncing : fatal exception in interrupt panic occurred, switching back to text console".
Call trace I get when Kernel panics:

Quote:
update_sd_lb_stats+0xcd/0x4b0
find_busiest_group+0x2e/0x420
enqueue_entity+0xcb/0x510
load_balance+0x7e/0x5e0
rebalance_domains+0xed/0x150
__do_softirq+0xdb/0x180
local_bh_enable_ip+0x90/0x90
<IRQ>
copy_to_user0x41/0x60
sys_gettimeofday+0x2a/0x70
sysenter_do_call0x12/0x20
Below is the full code:

Code:
 #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/kdev_t.h>
    #include <linux/wait.h>
    #include <linux/errno.h>
    #include <linux/slab.h>     /* kmalloc() */
    #include <linux/tty_driver.h>
    #include <linux/tty.h>
    #include <linux/tty_flip.h>
    #include <linux/mutex.h>
    #include <linux/serial.h>
    #include <linux/sched.h>
    #include <asm/uaccess.h>
    #include <asm/termios.h>


    MODULE_LICENSE("GPL");

    #define MS_TTY_MAJOR 250    //test value
    #define MS_TTY_NUM_DEV 2
    #define DELAY_TIME      HZ * 2      /* 2 seconds per character */
    #define TINY_DATA_CHARACTER 't'

    static int major_num;
    //static int minor_num=0;
    //static int num_tty_dev=2;

    /* Below structure is a wrapper for device specific fields */
    struct ms_tty_serial {
        struct tty_struct   *tty;       /* pointer to the tty for this device */
        int         open_count; /* number of times this port has been opened */
        struct semaphore    sem;        /* locks this structure */
        struct timer_list   *timer;

    };

    static struct ms_tty_serial *ms_tty_table[MS_TTY_NUM_DEV];  /* initially all NULL */

    static void ms_tty_timer(unsigned long timer_data)
    {
        struct ms_tty_serial *ms_ptr = (struct ms_tty_serial *)timer_data;
        struct tty_struct *tty;
        char data[1] = {TINY_DATA_CHARACTER};


    int data_size = 1;

        if (!ms_ptr)
            return;

        tty = ms_ptr->tty;
        tty->low_latency=1;
        /* send the data to the tty layer for users to read.  This doesn't
         * actually push the data through unless tty->low_latency is set */
        tty_buffer_request_room (tty, data_size);
            tty_insert_flip_string(tty, data, data_size);
            tty_flip_buffer_push(tty);

        /* resubmit the timer again */
        ms_ptr->timer->expires = jiffies + DELAY_TIME;
        add_timer(ms_ptr->timer);
    }



    //// Define the open function ////

   static int tty_ms_open(struct tty_struct *tty_this, struct file *file_this)
{

    printk(KERN_ALERT "tty_ms driver: OPENED ...\n");
    struct ms_tty_serial *ms_ptr;
    struct timer_list *timer;
    int index;

    /* initialize the pointer in case something fails */
    tty_this->driver_data = NULL;

    /* get the serial object associated with this tty pointer */
    index = tty_this->index;
    ms_ptr = ms_tty_table[index];
    if (ms_ptr == NULL) {
        /* first time accessing this device, create it */
        ms_ptr = kmalloc(sizeof(*ms_ptr), GFP_KERNEL);
        if (!ms_ptr)
            return -ENOMEM;

//      init_MUTEX(&ms_ptr->sem);       /* didn't work for this kernel version 3.5.0 */

        #ifndef init_MUTEX      /* sema_init is to be used for kernel 2.6.37 and above */
        sema_init(&ms_ptr->sem,1);
        #else
        init_MUTEX(&ms_ptr->sem);
        #endif

        ms_ptr->open_count = 0;
        ms_ptr->timer = NULL;

        ms_tty_table[index] = ms_ptr;
    }

    down(&ms_ptr->sem);

    /* save our structure within the tty structure */
    tty_this->driver_data = ms_ptr;
    ms_ptr->tty = tty_this;
    ms_ptr->filp = file_this;       // to be tried
    ++ms_ptr->open_count;
    if (ms_ptr->open_count == 1) {
        /* this is the first time this port is opened */
        /* do any hardware initialization needed here */

        /* create timer and submit it */
        if (!ms_ptr->timer) {
            timer = kmalloc(sizeof(*timer), GFP_KERNEL);
            if (!timer) {
                up(&ms_ptr->sem);
                return -ENOMEM;
            }
            ms_ptr->timer = timer;
        }

        init_timer (ms_ptr->timer);     // to be tried
        ms_ptr->timer->data = (unsigned long )ms_ptr;
        ms_ptr->timer->expires = jiffies + DELAY_TIME;
        ms_ptr->timer->function = ms_tty_timer;
        add_timer(ms_ptr->timer);
    }

    up(&ms_ptr->sem);
    return 0;
}

    //// Define the close function ////

    static void do_close(struct ms_tty_serial *ms_ptr)
    {
        down(&ms_ptr->sem);

        if (!ms_ptr->open_count) {
            /* port was never opened */
            goto exit;
        }

        --ms_ptr->open_count;
        if (ms_ptr->open_count <= 0) {
            /* The port is being closed by the last user. */
            /* Do any hardware specific stuff here */

            /* shut down our timer */
            del_timer(ms_ptr->timer);
        }
    exit:
        up(&ms_ptr->sem);
    }

    static void tty_ms_close(struct tty_struct *tty_this, struct file *file_this)
    {

        printk(KERN_ALERT "tty_ms driver: CLOSING ...\n");
        struct ms_tty_serial *ms_ptr = tty_this->driver_data;

        if (ms_ptr)
            do_close(ms_ptr);
    }


    //// Define the write function ////

    static int tty_ms_write(struct tty_struct *tty_this, const unsigned char *tty_buff, int count)
    {

        printk(KERN_ALERT "tty_ms driver: WRITING ...\n");
    struct ms_tty_serial *ms_ptr = tty_this->driver_data;
        int i;
        int retval = -EINVAL;

        if (!ms_ptr)
            return -ENODEV;

        down(&ms_ptr->sem);

        if (!ms_ptr->open_count)
            /* port was not opened */
            {
            up(&ms_ptr->sem);
            return retval;
            }
        /* fake sending the data out a hardware port by
         * writing it to the kernel debug log.
         */
        printk(KERN_DEBUG "%s - ", __FUNCTION__);
        for (i = 0; i < count; ++i)
            printk("%02x ", tty_buff[i]);
        printk("\n");
        return 0;

    }

    // Define the operations for tty driver in tty_operations struct //

    static struct tty_operations tty_ms_ops = {
    .open = tty_ms_open,
    .close = tty_ms_close,
    .write = tty_ms_write,
    //.set_termios = tty_ms_set_termios,
    };

    ///////////////////////////////////////// Module Initialization Starts ////////////////////////////////////

    static struct tty_driver *tty_ms_driver;

    static int tty_ms_init(void)
    {
    //  static int result;
        static int retval,iter;

    // Allocate the tty_driver struct for this driver //

        tty_ms_driver = alloc_tty_driver(MS_TTY_NUM_DEV);
        if (!tty_ms_driver)
        return -ENOMEM;         // Error NO Memory , allocation failed
        else
        printk(KERN_INFO "tty_driver structure allocated..!!");     //debug line

    // Initialize the allocated tty_driver structure //
        tty_ms_driver->magic=TTY_DRIVER_MAGIC;
        tty_ms_driver->owner = THIS_MODULE;
        tty_ms_driver->driver_name = "tty_ms";
        tty_ms_driver->name = "ttyms";
        tty_ms_driver->major = MS_TTY_MAJOR,
        tty_ms_driver->type = TTY_DRIVER_TYPE_SERIAL,
        tty_ms_driver->subtype = SERIAL_TYPE_NORMAL,
        tty_ms_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV,
        tty_ms_driver->init_termios = tty_std_termios;
        tty_ms_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
        tty_set_operations(tty_ms_driver, &tty_ms_ops);
        printk(KERN_INFO "allocated tty_driver structure -INITIALIZED.");   //debug line
    //// Register this driver with the tty core ////

        retval = tty_register_driver(tty_ms_driver);
        if (retval) {
        printk(KERN_ERR "failed to register tty_ms driver\n tty registration returned %d", retval);
        put_tty_driver(tty_ms_driver);
        return retval;
        }

    //// Register the tty devices(nodes) with the tty core ////

        for (iter = 0; iter < MS_TTY_NUM_DEV ; ++iter)
        tty_register_device(tty_ms_driver, iter, NULL);
        return 0;       // All initializations done

    }           // init func ends

    ///////////////////////////////////////// Module Initialization Ends ////////////////////////////////////

    ///////////////////////////////////////// Module cleanup Starts ////////////////////////////////////

    static void tty_ms_terminate(void)
    {
        static int iter;
        struct ms_tty_serial *tty_ser;
        printk(KERN_ALERT "tty_ms driver: Unloading...\n");

        for(iter=1;iter<=MS_TTY_NUM_DEV;iter++)
         tty_unregister_device(tty_ms_driver,iter);   //unregister all the devices, from tty layer 
        tty_unregister_driver(tty_ms_driver);       // Now unregister the driver from tty layer

        /* shut down all of the timers and free the memory */
        for (iter = 0; iter < MS_TTY_NUM_DEV; ++iter) {
            tty_ser = ms_tty_table[iter];
            if (tty_ser) {
                /* close the port */
                while (tty_ser->open_count)
                    do_close(tty_ser);

                /* shut down our timer and free the memory */
                del_timer(tty_ser->timer);
                kfree(tty_ser->timer);
                kfree(tty_ser);
                ms_tty_table[iter] = NULL;
            }
        }   

         dev_t devno=MKDEV(major_num,0);  // wrap major/minor numbers in a dev_t structure , to pass for deassigning.
         unregister_chrdev_region(devno,MS_TTY_NUM_DEV); 

    }

    ///////////////////////////////////////// Module cleanup ends ////////////////////////////////////

    module_init(tty_ms_init);
    module_exit(tty_ms_terminate);
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Linux Driver - how to read/write a block or character device in kernel space (procfs) stybi Linux - Kernel 1 01-27-2011 06:26 AM
read problem in tty driver valmorcz Programming 1 01-17-2010 09:19 PM
TTY driver for 2.6.28 kernel core branko Linux - Newbie 1 06-11-2009 06:48 AM
How to make serial line descipline or write driver to user tty layer Kashyap Linux - Software 1 02-20-2006 08:49 AM
installed pcmcia driver and orinoco gold card kernel panics hotgirl101 Linux - Hardware 2 04-13-2004 09:25 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - Software > Linux - Kernel

All times are GMT -5. The time now is 07:53 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration