我是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*"
以下是完整的代码。任何指导都非常值得期待。
10May2013 :我尝试在open函数中初始化计时器,下面这次是“内核恐慌 - 不同步:在发生中断恐慌时发生致命异常,切换回文本控制台”的调用跟踪:
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);