4

我是 Linux 编程的初学者,并在练习时尝试了一些设备驱动程序示例。下面的代码(精简版tiny_tty.c)使用 insmod 完美加载,我可以在 中看到它/proc/tty/drivers/proc/modules并且设备节点正在 /dev 下创建。当我尝试像这样写入设备文件echo "abcd" > /dev/ttyms0(我希望这很好)或像这样读取cat /dev/ttyms0时,内核会在屏幕上出现调用跟踪而发生恐慌。我在 Ubuntu 下使用内核 3.5.0。不幸的是,我无法捕获跟踪,因为当它恐慌时,我别无选择,只能使用电源按钮重新启动。我相信计时器存在一些问题,因为跟踪在顶部显示一行: "*kernel bug at /build/buildd/linux-3.5.0/kernel/timer.c:901*“,然后是调用跟踪,然后 "*EIP is at add_timer+0x18/0x20*" 是完整的代码。非常感谢任何指导。

2013 年 5 月 10 日:我尝试在打开函数中初始化计时器,这一次下面是“内核恐慌 - 不同步:发生中断恐慌中的致命异常,切换回文本控制台”的调用跟踪:

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



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

0 回答 0