如何把字元裝置抽象成檔案?
open()函數,在檔案系統中找到指定檔案的操作接口,綁定到程序
task_srtuct->files_struct->fd_array[]->file_operations
思路
把底層寄存器配置操作放在檔案操作接口裡面,建立一個檔案綁定該檔案操作接口,應用程式通過操作指定檔案來設定底層寄存器
基本接口實作
- 查原理圖,資料手冊,确定底層需要配置的寄存器
- 類似裸機開發
- 實作一個檔案的底層操作接口,這是檔案的基本特征
struct file_operations //fops
/include/linux/fs.h
幾乎所有成員都是函數指針,用來實作檔案的具體操作
驅動層原理
把file_operations檔案操作接口注冊到核心,核心通過主次裝置号來登記記錄它
- 構造驅動基本對象:struct cdev,裡面記錄具體的file_operations
cdev_init()
- 兩個hash表
- chrdevs:登記裝置号
__register_chrdev_region()
- cdev_map->probe:儲存驅動基本對象struct cdev
cdev_add()
檔案系統層原理
mknod指令+主從裝置号
- 建構一個新的裝置檔案
- 通過主次裝置号在cdev_map中找到cdev->file_operations
- 把cdev->file_operations綁定到新的裝置檔案中
到這裡,應用程式就可以使用open()、write()、read()等函數來控制裝置檔案了
/*
* RTC client/driver for the Maxim/Dallas SD2405 Real-Time Clock over I2C
*
* Based on code by Randy Vinson <[email protected]>,
* which was based on the m41t00.c by Mark Greer <[email protected]>.
*
* Copyright (C) 2014 Rose Technology
* Copyright (C) 2006-2007 Freescale Semiconductor
*
* 2005 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
/*
* It would be more efficient to use i2c msgs/i2c_transfer directly but, as
* recommened in .../Documentation/i2c/writing-clients section
* "Sending and receiving", using SMBus level communication is preferred.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/pm.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/miscdevice.h>
#include <linux/reboot.h>
#include <linux/watchdog.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/kthread.h>
#define DEBUG
#include <linux/device.h>
#undef DEBUG
#include <linux/proc_fs.h>
#include <linux/delay.h>
#define DEV_NAME "test_char_dev"
#define DEV_CNT (1)
#define BUFF_SIZE 128
//裝置類
struct class* chr_test_class = NULL;
//裝置節點
struct device* chr_test_device = NULL;
//資料緩沖區
static char vbuf[BUFF_SIZE] = {0};
static int chr_dev_open(struct inode *inode, struct file *filp)
{
printk("OPEN!!!\n");
printk("f_pos:%d\r\n",filp->f_pos);
return 0;
}
static int chr_dev_release(struct inode *inode, struct file *filp)
{
mdelay(2000);
printk("RELEASE!!\n");
return 0;
}
ssize_t chr_dev_write(struct file *filp, const char __user * buf, size_t count, loff_t *ppos)
{
printk("write!!\r\n");
unsigned long p = *ppos;
int ret;
int tmp = count;
if (p > BUFF_SIZE)
return 0;
if (tmp > BUFF_SIZE - p)
tmp = BUFF_SIZE - p;
ret = copy_from_user(vbuf, buf, tmp);
*ppos += tmp;
printk("now buf:%s tmp:%d f_pos:%d\n",vbuf,tmp,filp->f_pos);
return tmp;
}
ssize_t chr_dev_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
printk("read!!\r\n");
unsigned long p = *ppos;
int ret;
int tmp = count;
if (p > BUFF_SIZE)
return 0;
if (tmp > BUFF_SIZE - p)
tmp = BUFF_SIZE - p;
ret = copy_to_user(buf, vbuf + p, tmp);
*ppos += tmp;
printk("now buf:%s p:%d\n",vbuf,p);
return tmp;
}
loff_t chr_dev_llseek(struct file *filp, loff_t off, int whence)
{
struct device* dev = filp->private_data;
loff_t newpos;
printk("whence:%d off:%d\r\n",whence,off);
switch (whence)
{
case SEEK_SET:
newpos = off;
break;
case SEEK_CUR:
newpos = filp->f_pos + off;
break;
case SEEK_END:
newpos = BUFF_SIZE + off;
break;
default:
return -EINVAL;
}
if(newpos < 0)
{
return -EINVAL;
}
filp->f_pos = newpos;
return newpos;
}
static struct file_operations chr_dev_fops = {
.owner = THIS_MODULE,
.open = chr_dev_open,
.release = chr_dev_release,
.write = chr_dev_write,
.read = chr_dev_read,
.llseek = chr_dev_llseek,
};
//定義裝置号
static dev_t devno;
//定義字元裝置結構體 char_dev
static struct cdev chr_dev;
static int __init chrdev_init(void)
{
int ret = 0;
printk("chrdev_init\n");
//第一步
//采用動态配置設定的方法,擷取裝置編号,次裝置号為0
//裝置名稱為 test_char_dev ,可通過指令 cat /proc/devices檢視
//DEV_CNT為1,目前隻申請一個裝置編号
ret = alloc_chrdev_region(&devno, 0, DEV_CNT, DEV_NAME);
if(ret < 0)
{
printk("fail to alloc devno\n");
goto alloc_err;
}
//第二步
//關聯字元裝置結構體cdev與檔案操作結構體file_operations
cdev_init(&chr_dev, &chr_dev_fops);
//第三步
//添加裝置至cdev_map散清單
ret = cdev_add(&chr_dev, devno, DEV_CNT);
if(ret < 0)
{
printk("fail to add cdev\n");
goto add_err;
}
//建立裝置類
if(chr_test_class == NULL)
chr_test_class = class_create(THIS_MODULE, DEV_NAME);
//建立裝置節點
chr_test_device = device_create(chr_test_class, NULL, devno, NULL, DEV_NAME);
return 0;
add_err:
//添加裝置失敗時,需要登出裝置号
unregister_chrdev_region(devno, DEV_CNT);
alloc_err:
return ret;
}
module_init(chrdev_init);
static void __exit chrdev_exit(void)
{
printk("chrdev exit\n");
unregister_chrdev_region(devno, DEV_CNT);
cdev_del(&chr_dev);
//删除裝置節點
device_destroy(chr_test_class, devno);
//銷毀裝置類
class_destroy(chr_test_class);
}
module_exit(chrdev_exit);
static const struct of_device_id chardev_test_dt_ids[] = {
{ .compatible = "me, chardev_test" },
{}
};
MODULE_DEVICE_TABLE(of, chardev_test_dt_ids);
MODULE_AUTHOR("reisen");
MODULE_LICENSE("GPL");