---簡介
Linux下的MISC簡單字符設備驅動雖然使用簡單,但卻不靈活。
只能建立主設備號為10的設備文件。字符設備比較容易理解,同時也能夠滿足大多數簡單的硬件設備,字符設備通過文件系 統中的名字來讀取。這些名字就是文件系統中的特殊文件或者稱為設備文件、文件系統的簡單結點,一般位於/dev/目錄下 使用ls進行查看會顯示以C開頭證明這是字符設備文件crw--w---- 1 root tty 4, 0 4月 14 11:05 tty0。第一個數字是主設備 號,第二個數字是次設備號。
---分配和釋放設備編號
1)在建立字符設備驅動時首先要獲取設備號,為此目的的必要的函數是register_chrdev_region,在linux/fs.h中聲明:int register_chrdev_region(dev_t first, unsigned int count, char *name);first是你想要分配的起始設備編號,first的次編號通 常是0,count是你請求的連續設備編號的總數。count如果太大會溢出到下一個主設備號中。name是設備的名字,他會出 現在/proc/devices 和sysfs中。操作成功返回0,如果失敗會返回一個負的錯誤碼。
2)如果明確知道設備號可用那麼上一個方法可行,否則我們可以使用內核動態分配的設備號int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);dev是個只輸出的參數,firstminor請求的第一個要用的次 編號,count和name的作用如上1)對於新驅動,最好的方法是進行動態分配
3)釋放設備號,void unregister_chrdev_region(dev_t first unsigned int count);
---文件操作file_operations結構體,內部連接了多個設備具體操作函數。該變量內部的函數指針指向驅動程序中的具體操 作,沒有對應動作的指針設置為NULL。
1)fops的第一個成員是struct module *owner 通常都是設置成THIS_MODULE。
linux/module.h中定義的宏。用來在他的操作還在被使用時阻止模塊被卸載。
2)loff_t (*llseek) (struct file *, loff_t, int);該方法用以改變文件中的當前讀/寫位置
返回新位置。
3)ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);該函數用以從設備文件
中讀取數據,讀取成功返回讀取的字節數。
4)ssize_t (*write) (struct file *, const char __user *,size_t , loff_t *);該函數用以向設備
寫入數據,如果成功返回寫入的字節數。
5)int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);ioctl系統調用提供
發出設備特定命令的方法。
6)int (*open) (struct inode *, struct file *);設備文件進行的第一個操作,打開設備文件。
7)int (*release) (struct inode *, struct file *);釋放文件結構函數指針。
一般初始化該結構體如下:
struct file_operations fops = {
.owner = THIS_MODULE, .llseek = xxx_llseek, .read = xxx_read, .write = xxx_write,
.ioctl = xxx_ioctl, .open = xxx_open, .release = xxx_release };
PS:以上的文件操作函數指針並不是全部,只是介紹了幾個常用的操作。
---文件結構
struct file定義在linux/fs.h中,是設備驅動中第二個最重要的數據結構,此處的file和
用戶空間程序中的FILE指針沒有關系。前者位於內核空間,後者位於用戶控件。
文件結構代表一個打開的文件。(他不特定給設備驅動;系統中每個打開的文件
有一個關聯的struct file在內核空間)。它由內核在open時創建,並可以傳遞給文件件
操作函數,文件關閉之後,內核釋放數據結構。
1)mode_t f_mode。確定文件讀寫模式
2)loff_t f_ops。當前讀寫位置
3)unsigned int f_flags 。文件標志,O_RDONLY、O_NONBLOCK,
4)struct file_operations *f_op。關聯文件相關操作
5)void *private_data。open系統調用設置該指針NULL,指向分配的數據。
6)struct dentry *f_dentry。關聯到文件的目錄入口dentry結構。
---inode結構
inode結構由內核在內部用來表示文件。它和代表打開文件描述符的文件結構是不
同的。inode結構包含大量關於文件的信息。作為通用規則,這個結構只有兩個成
員對驅動代碼有作用。
dev_t i_rdev。對於代表設備文件的節點,這個成員包含實際的設備編號。
struct cdev *i_cdev。內核內部結構,代表字符設備。
---字符設備注冊
在內核調用你的設備操作前,你編寫分配並注冊一個或幾個struct cdev.
struct cdev *my_cdev = cdev_alloc(); my_cdev->ops = &my_fops;
或者定義成static均可。
對定義的cdev變量進行初始化,可以使用專門的函數,或者使用如上的方法。
cdev_init( my_cdev, &my_fops); 其實上邊的兩行代碼就是做了這個函數的工作。
最後告訴內核該cdev。
cdev_add(struct cdev *dev, dev_t num, unsigned int count);
/*上述總結,到此關於設備文件相關的結構數據以及如何注冊銷毀等操作相關的
函數基本上都已經介紹完畢。主要的還是要設計具體操作的函數來實現具體的
邏輯操作*/
以下代碼整理、摘錄自《Android深度探索HAL與驅動開發-李寧》LED驅動篇
#include
#include
#include
#include
#include
#include
#include
#deifne DEVICE_NAME "s3c6410_leds"
#define DEVICE_COUNT 1
#define S3C6410_LEDS_MAJOR 0
#define S3C6410_LEDS_MINOR 234
#define PARAM_SIZE 3
static int major = S3C6410_LEDS_MAJOR;
static int minor = S3C6410_LEDS_MINOR;
static dev_t dev_number;
static int leds_state = 1;
static char *params[] = {"string1","string2","string3"};
static iint param_size = PARAM_SIZE;
static struct class *leds_class = NULL;
static int s3c6410_leds_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
unsigned tmp;
case 0:
case 1:
if (arg > 4)
return -EINVAL;
tmp = ioread32 (S3C64XX_GPMDAT);
if (cmd == 1)
tmp &= (~(1 << arg));
else
tmp |= (1 << arg);
iowrite32 (tmp, S3C64XX_GPMDAT);
return 0;
default : return -EINVAL;
}
}
static ssize_t s3c6410_leds_write (struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
unsigned tmp = count;
unsigned long i = 0;
memset(mem, 0, 4);
if (count > 4)
tmp = 4;
if (copy_from_user (mem, buf, tmp) )
return -EFAULT;
else{
for( i=0; i<4; i++)
{
tmp = ioread32(S3C64XX_GPMDAT);
if (mem[i] == '1')
tmp &= (~(1 << i));
else
tmp |= (1 << i);
iowrite32(tmp, S3C64XX_GPMDAT);
}
return count;
}
}
static struct file_operations dev_fops =
{.owner = THIS_MODULE, .unlocked_ioctl = s3c6410_leds_ioctl, .write = s3c6410_leds_write};
static struct cdev leds_cdev;
static int leds_create_device(void)
{
int ret = 0;
int err = 0;
cdev_init (&leds_cdev, &dev_fops);
leds_cdev.owner = THIS_MODULE;
if (major > 0)
{
dev_number = MKDEV(major,minor);
err = register_chrdev_region(dev_number, DEVICE_COUNT, DEVICE_NAME);
if (err < 0)
{
printk(KERN_WANRING "register_chrdev_region errorn");
return err
}
}
else{
err = alloc_chrdev_region(&leds_cdev.dev, 10, DEVICE_COUNT, DEVICE_NAME);
if(err < 0)
{
printk (KERN_WARNING "alloc_chrdev_region errorn");
retur