LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Kernel (https://www.linuxquestions.org/questions/linux-kernel-70/)
-   -   Kernel panics : trying to write / read on tiny tty driver (https://www.linuxquestions.org/questions/linux-kernel-70/kernel-panics-trying-to-write-read-on-tiny-tty-driver-4175461609/)

diwsdiwa 05-12-2013 03:06 AM

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);



All times are GMT -5. The time now is 02:01 PM.