Linux下的MISC簡單字符設(shè)備驅(qū)動雖然使用簡單,但卻不靈活。
只能建立主設(shè)備號為10的設(shè)備文件。字符設(shè)備比較容易理解,同時也能夠滿足大多數(shù)簡單的硬件設(shè)備,字符設(shè)備通過文件系 統(tǒng)中的名字來讀取。這些名字就是文件系統(tǒng)中的特殊文件或者稱為設(shè)備文件、文件系統(tǒng)的簡單結(jié)點,一般位于/dev/目錄下 使用ls進行查看會顯示以C開頭證明這是字符設(shè)備文件crw--w---- 1 root tty 4, 0 4月 14 11:05 tty0。第一個數(shù)字是主設(shè)備 號,第二個數(shù)字是次設(shè)備號。
---分配和釋放設(shè)備編號
1)在建立字符設(shè)備驅(qū)動時首先要獲取設(shè)備號,為此目的的必要的函數(shù)是register_chrdev_region,在linux/fs.h中聲明:int register_chrdev_region(dev_t first, unsigned int count, char *name);first是你想要分配的起始設(shè)備編號,first的次編號通 常是0,count是你請求的連續(xù)設(shè)備編號的總數(shù)。count如果太大會溢出到下一個主設(shè)備號中。name是設(shè)備的名字,他會出 現(xiàn)在/proc/devices 和sysfs中。操作成功返回0,如果失敗會返回一個負的錯誤碼。
2)如果明確知道設(shè)備號可用那么上一個方法可行,否則我們可以使用內(nèi)核動態(tài)分配的設(shè)備號int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);dev是個只輸出的參數(shù),firstminor請求的第一個要用的次 編號,count和name的作用如上1)對于新驅(qū)動,最好的方法是進行動態(tài)分配
3)釋放設(shè)備號,void unregister_chrdev_region(dev_t first unsigned int count);
---文件操作file_operations結(jié)構(gòu)體,內(nèi)部連接了多個設(shè)備具體操作函數(shù)。該變量內(nèi)部的函數(shù)指針指向驅(qū)動程序中的具體操 作,沒有對應(yīng)動作的指針設(shè)置為NULL。
1)fops的第一個成員是struct module *owner 通常都是設(shè)置成THIS_MODULE。
linux/module.h中定義的宏。用來在他的操作還在被使用時阻止模塊被卸載。
2)loff_t (*llseek) (struct file *, loff_t, int);該方法用以改變文件中的當(dāng)前讀/寫位置
返回新位置。
3)ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);該函數(shù)用以從設(shè)備文件
中讀取數(shù)據(jù),讀取成功返回讀取的字節(jié)數(shù)。
4)ssize_t (*write) (struct file *, const char __user *,size_t , loff_t *);該函數(shù)用以向設(shè)備
寫入數(shù)據(jù),如果成功返回寫入的字節(jié)數(shù)。
5)int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);ioctl系統(tǒng)調(diào)用提供
發(fā)出設(shè)備特定命令的方法。
6)int (*open) (struct inode *, struct file *);設(shè)備文件進行的第一個操作,打開設(shè)備文件。
7)int (*release) (struct inode *, struct file *);釋放文件結(jié)構(gòu)函數(shù)指針。
一般初始化該結(jié)構(gòu)體如下:
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:以上的文件操作函數(shù)指針并不是全部,只是介紹了幾個常用的操作。
---文件結(jié)構(gòu)
struct file定義在linux/fs.h中,是設(shè)備驅(qū)動中第二個最重要的數(shù)據(jù)結(jié)構(gòu),此處的file和
用戶空間程序中的FILE指針沒有關(guān)系。前者位于內(nèi)核空間,后者位于用戶控件。
文件結(jié)構(gòu)代表一個打開的文件。(他不特定給設(shè)備驅(qū)動;系統(tǒng)中每個打開的文件
有一個關(guān)聯(lián)的struct file在內(nèi)核空間)。它由內(nèi)核在open時創(chuàng)建,并可以傳遞給文件件
操作函數(shù),文件關(guān)閉之后,內(nèi)核釋放數(shù)據(jù)結(jié)構(gòu)。
1)mode_t f_mode。確定文件讀寫模式
2)loff_t f_ops。當(dāng)前讀寫位置
3)unsigned int f_flags 。文件標(biāo)志,O_RDONLY、O_NONBLOCK,
4)struct file_operations *f_op。關(guān)聯(lián)文件相關(guān)操作
5)void *private_data。open系統(tǒng)調(diào)用設(shè)置該指針NULL,指向分配的數(shù)據(jù)。
6)struct dentry *f_dentry。關(guān)聯(lián)到文件的目錄入口dentry結(jié)構(gòu)。
---inode結(jié)構(gòu)
inode結(jié)構(gòu)由內(nèi)核在內(nèi)部用來表示文件。它和代表打開文件描述符的文件結(jié)構(gòu)是不
同的。inode結(jié)構(gòu)包含大量關(guān)于文件的信息。作為通用規(guī)則,這個結(jié)構(gòu)只有兩個成
員對驅(qū)動代碼有作用。
dev_t i_rdev。對于代表設(shè)備文件的節(jié)點,這個成員包含實際的設(shè)備編號。
struct cdev *i_cdev。內(nèi)核內(nèi)部結(jié)構(gòu),代表字符設(shè)備。
---字符設(shè)備注冊
在內(nèi)核調(diào)用你的設(shè)備操作前,你編寫分配并注冊一個或幾個struct cdev.
struct cdev *my_cdev = cdev_alloc(); my_cdev->ops = &my_fops;
或者定義成static均可。
對定義的cdev變量進行初始化,可以使用專門的函數(shù),或者使用如上的方法。
cdev_init( my_cdev, &my_fops); 其實上邊的兩行代碼就是做了這個函數(shù)的工作。
最后告訴內(nèi)核該cdev。
cdev_add(struct cdev *dev, dev_t num, unsigned int count);
/*上述總結(jié),到此關(guān)于設(shè)備文件相關(guān)的結(jié)構(gòu)數(shù)據(jù)以及如何注冊銷毀等操作相關(guān)的
函數(shù)基本上都已經(jīng)介紹完畢。主要的還是要設(shè)計具體操作的函數(shù)來實現(xiàn)具體的
邏輯操作*/
以下代碼整理、摘錄自《Android深度探索HAL與驅(qū)動開發(fā)-李寧》LED驅(qū)動篇
#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");
return err;
}
major = MAJOR(leds_cdev.dev);
major = MINOR(leds_cdev.dev);
dev_number = leds_cdev.dev;
}
ret = cdev_add(&leds_cdev,dev_number, DEVICE_COUNT);
leds_class = class_create (THIS_MODULE, DEVICE_NAME);
device_create (leds_class, NULL, dev_number, NULL, DEVICE_NAME);
return ret;
}
static void leds_init_gpm(int leds_default){
int tmp = 0;
tmp = ioread32(S3C64XX_GPMCON);
tmp &= (~0xffff);
tmp |= 0x1111;
iowrite32(tmp,S3C64XX_GPMCON);
tmp = ioread32(S3C64XX_GPMPUD);
tmp &= (~0XFF);
tmp |= 0xaa;
iowrite32(tmp,S3C64XX_GPMPUD);
tmp = ioread32(S3C64XX_GPMDAT);
tmp &= (~0xf);
tmp |= leds_default;
iowrite32(tmp, S3C64XX_GPMDAT);
}
static leds_init( void)
{
int ret;
ret = leds_create_device();
leds_init_gpm (~leds_state);
printk(DEVICE_NAME"tinitializedn");
return ret;
}
static void leds_destroy_device(void)
{
device_destroy(leds_class, dev_number);
if(leds_class)
class_destroy(leds_class);
unregister_chrdev_region(dev_number, DEVICE_NAME);
}
static void leds_exit(void)
{
leds_destroy_device();
printk(DEVICE_NAME"texitn");
}
module_init(leds_init);
module_exit(leds_exit);
module_param(leds_state, int, S_IRUGO|S_IWUSR);
module_param_array(params, charp, ?m_size, S_IRUGO|S_IWUSR);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lining");