字符設備是最多的硬件設備,如常見的touchscreen, lcd等。
一般認為數據需要順序訪問的都屬於字符設備,網卡也可以認為是一個字符設備,不同之處在於網絡需要許多與協議棧相關的處理。而塊設備的特點是:讀寫數據成塊進行,但是更重要的是能夠被隨即訪問。
2.6內核比2.4內核操作要復雜一些,但是從結構以及原理上更能避免一些不必要的問題,增強了科學性。
以下為筆者近期一個驅動實例,希望能以此闡述一二。
驅動開發以模塊形式進行,因為最終需要驅動服務於我們的上層軟件應用,這要求必須使驅動運行在內核空間,操作系統在運行時,我們需要使用一個固定方式將我們的驅動加入到內核,模塊就為我們提供了這樣的一個方法。一般模塊需要三個重要部分:
MODULE_LICENSE("GPL");
module_init(dvr_tdm_init);
module_exit(dvr_tdm_exit);
tdm_init與tdm_exit為函數名字,需要在模塊中具體實現,如下:
static struct file_operations dvr_tdm_fops =
{
read: dvr_tdm_read,
write: dvr_tdm_write,
open: dvr_tdm_open,
release: dvr_tdm_release,
};
#if 0
這個結構包括很多函數指針,增加這些函數的實現,可以實現很多高級功能,
如:
1,實現設備的阻塞於非阻塞 (主要使用DECLARE_WAIT_QUEUE_HEAD(name);
init_waitqueue_head(wait_queue_head_t *name);
wait_event_interruptible(queue, condition)
void wake_up_interruptible(wait_queue_head_t *queue); 詳見:並發與競爭
2,並發與競爭
3,實現對select函數的支持。
4,實現對內核空間的mmap。
5,對硬件的io控制等。
#endif
static struct cdev * dvr_tdm_cdev;
static dev_t tdm_dev_t;
static int __init dvr_tdm_init(void)
{
ret = alloc_chrdev_region(&tdm_dev_t, 0, DVR_TDM_COUNT, DRV_NAME);
if (ret != 0)
{
printk(KERN_ERR " alloc_chrdev_regin failed\n");
goto out;
}
dvr_tdm_cdev = cdev_alloc();
dvr_tdm_cdev->ops = &dvr_tdm_fops;
dvr_tdm_cdev->owner = THIS_MODULE;
//init driver interfaces
cdev_init(dvr_tdm_cdev, &dvr_tdm_fops);
debug("dvr_tdm_init: chr device initialized\n");
ret = cdev_add(dvr_tdm_cdev, tdm_dev_t, DVR_TDM_COUNT);
if (!ret)
{
printk("dvr_tdm_init: chr device added\n");
}
else
{
printk("dvr_tdm_init: cannot add chr device\n");
}
out:
return ret;
}
static void __exit dvr_tdm_exit(void)
{
unregister_chrdev_region(tdm_dev_t, DVR_TDM_COUNT);
cdev_del(dvr_tdm_cdev);
}
函數名字前的__init與__exit作用為:__exit聲明將該段代碼放到內核啟動init區內,該區內代碼只運行一次,運行結束後,代碼被丟棄。標記為__exit的代碼直接丟棄。這兩個聲明都是給當模塊加入到內核作准備的,如果只以獨立模塊運行,上述聲明沒有什麼意義。
以下是驅動的read write open release函數的基本實現。
static int dvr_tdm_open(struct inode *inode, struct file *filp)
{
if(!try_module_get(THIS_MODULE)) // 如果模塊已經插入內核,則遞增該模塊引用計數;如果該模塊還沒有插入內核,則返回0表示出錯。
printk("try_module_get error\n");
debug("dvr_tdm_open: driver run ok\n");
return 0;
}
static ssize_t dvr_tdm_write (struct file *file, const char __user *buf, size_t count,
loff_t *offset)
{
short signed int pcm_buffer[TDM_FRAME_LENGTH];
int ret;
ret=copy_from_user(pcm_buffer, buf, count);
if(ret)
printk("dvr_tdm_write: there are %x bytes can't read from user\n", ret);
//......
return 0;
}
static int dvr_tdm_release(struct inode *inode, struct file *filp)
{
module_put(THIS_MODULE);
debug("dvr_tdm_release: driver release ok\n");
return 0;
}
static ssize_t dvr_tdm_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
short signed int pcm_buffer[TDM_FRAME_LENGTH];
int ret;
//....得到數據
ret=copy_to_user(buf, pcm_buffer, TDM_FRAME_LENGTH);
if(ret)
printk("dvr_tdm_read: there are %x bytes can't read for user\n", ret);
return 0;
}
Makefile:
ifneq ($(KERNELREASE),)
obj-m += mpc8315_tdm.o
else
KERNELDIR ?= /home/wilson/ltib/rpm/BUILD/linux/build
obj-m += dvr_tdm.o
dvr_tdm-objs := tdm_driver.o
PWD := $(shell pwd)
ALL:
$(MAKE) ARCH=powerpc CROSS_COMPILE=powerpc-e300c3-linux-gnu- O=build -C $(KERNELDIR) M=$(PWD) modules
clean:
rm *.o *.ko
endif
設備節點的創建:
#! /bin/sh
module="dvr_tdm"
device="dvr_tdm"
mode=664
error="Error: Cannot find dvr tdm device"
insmod ./$module.ko $* || exit 1
major=`cat /proc/devices | grep $device | awk '{print $1}'`
if [ -z $major ]
then echo $error; exit 1
fi
rm -f /dev/${device}