應用程序write函數的使用:
char *p = “hello,world”;
write(fd, p, 12); //將數據寫入到設備
底層驅動write接口
struct file_operations {
ssize_t (*write) (struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos);
};
write接口作用:用於寫設備,將數據寫入到設備中
與應用程序write的調用關係:
應用程序調用write->...->調用驅動write接口
參數:
file:文件指針
buf:保存用戶緩衝區的首地址(p),在驅動程序中不能直接訪問這個buf,如果驅動程序要向從用戶空間將數據從buf拷貝到內核空間,必須利用內核提供的內存拷貝函數
count:用戶要寫入的字節數,例如12字節
ppos:保存寫的位置信息,例如
獲取上一次的寫位置:
loff_t pos = *ppos;
假如這次成功寫了12字節;
最後要更新寫位置信息:
*ppos = pos + 12;
切記:對於write接口的第二個參數buf,這個buf指針保存的是用戶緩衝區的首地址,在內核空間不能直接訪問操作,需要利用內核的內存拷貝函數,將用戶數據拷貝到內核空間,這個內存拷貝函數:
unsigned long copy_from_user(void *to,
void __user *from,
unsigned long n)
作用:將用戶緩衝區的數據拷貝到內核緩衝區中
參數:
to:目的地址,傳遞內核緩衝區的首地址
from:源地址,傳遞用戶緩衝區的首地址(buf)
n:要拷貝的字節數
將來只要看到__user修飾的指針,就不能在驅動中直接訪問操作,必須利用內存拷貝函數!
案例:編寫字符設備驅動,提供write接口,將用戶空間的數據寫入到內核空間
int udata = 0x5555;
write(fd, &udata, sizeof(udata));
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h> //struct file_operations
#include <linux/cdev.h> //struct cdev + 設備號
#include <asm/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/uaccess.h> //copy_to_user
//聲明描述LED硬件相關的數據結構
struct led_resource {
char *name;
int gpio;
};
//定義初始化LED硬件信息
static struct led_resource led_info[] = {
[0] = {
.name = "LED1",
.gpio = S5PV210_GPC0(3)
},
[1] = {
.name = "LED2",
.gpio = S5PV210_GPC0(4)
}
};
//定義設備號
static dev_t dev;
//定義字符設備對象
static struct cdev led_cdev;
//調用關係:應用程序open->....->led_open
static int led_open(struct inode *inode, struct file *file)
{
int i;
for(i = 0; i < ARRAY_SIZE(led_info); i++)
gpio_set_value(led_info[i].gpio, 1);
printk("%s\n", __func__);
return 0; //執行成功返回0,執行失敗返回負值
}
//調用關係:應用程序close->...->led_close
static int led_close(struct inode *inode, struct file *file)
{
int i;
for(i = 0; i < ARRAY_SIZE(led_info); i++)
gpio_set_value(led_info[i].gpio, 0);
printk("%s\n", __func__);
return 0; //執行成功返回0,執行失敗返回負值
}
//調用關係:應用程序read->...->led_read
static ssize_t led_read(struct file *file,
char __user *buf,
size_t count,
loff_t *ppos)
{
//定義初始化內核緩衝區(存儲空間再後1G虛擬內存中)
int kdata = 0x5555;
//將內核數據上報給用戶
//切記:buf雖然保存的用戶緩衝區的首地址,但不能直接訪問
//*(int *)buf = kdata;錯誤
copy_to_user(buf, &kdata, sizeof(kdata));
printk("%s\n", __func__);
return sizeof(kdata); //失敗返回負值,成功返回實際讀取的字節數
}
//調用關係:應用程序write->...->最終調用led_write
static ssize_t led_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
//定義內核緩衝區
int kdata;
//拷貝用戶數據到內核
copy_from_user(&kdata, buf, sizeof(kdata));
printk("%s:從用戶寫入的數據 kdata = %#x\n", __func__, kdata);
return count; //失敗返回負值,成功返回寫入的字節數
}
//定義初始化硬件操作方法
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open, //打開設備
.release = led_close, //關閉設備
.read = led_read, //讀取設備
.write = led_write //寫設備
};
static int led_init(void)
{
int i;
//申請設備號
alloc_chrdev_region(&dev, 0, 1, "tarena");
//初始化字符設備對象
cdev_init(&led_cdev, &led_fops);
//註冊字符設備對象到內核
cdev_add(&led_cdev, dev, 1);
//申請GPIO資源和配置GPIO爲輸出口,輸出0(省電)
for (i = 0; i < ARRAY_SIZE(led_info); i++) {
gpio_request(led_info[i].gpio, led_info[i].name);
gpio_direction_output(led_info[i].gpio, 0);
}
return 0;
}
static void led_exit(void)
{
int i;
//輸出0,釋放GPIO資源
for (i = 0; i < ARRAY_SIZE(led_info); i++) {
gpio_set_value(led_info[i].gpio, 0);
gpio_free(led_info[i].gpio);
}
//卸載字符設備對象
cdev_del(&led_cdev);
//釋放設備號
unregister_chrdev_region(dev, 1);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd;
int udata = 0x5555; //定義用戶緩衝區
//打開設備
//open->....->調用led_open
fd = open("/dev/myled", O_RDWR);
if (fd < 0) {
printf("打開設備失敗!\n");
return -1;
}
//write->...->調用led_write
write(fd, &udata, sizeof(udata));
//關閉設備
//close->...->調用led_close
close(fd);
return 0;
}
案例:用戶寫1,開所有的燈;用戶寫0,關所有的燈;
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h> //struct file_operations
#include <linux/cdev.h> //struct cdev + 設備號
#include <asm/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/uaccess.h> //copy_to_user
//聲明描述LED硬件相關的數據結構
struct led_resource {
char *name;
int gpio;
};
//定義初始化LED硬件信息
static struct led_resource led_info[] = {
[0] = {
.name = "LED1",
.gpio = S5PV210_GPC0(3)
},
[1] = {
.name = "LED2",
.gpio = S5PV210_GPC0(4)
}
};
//定義設備號
static dev_t dev;
//定義字符設備對象
static struct cdev led_cdev;
//調用關係:應用程序open->....->led_open
static int led_open(struct inode *inode, struct file *file)
{
int i;
for(i = 0; i < ARRAY_SIZE(led_info); i++)
gpio_set_value(led_info[i].gpio, 1);
printk("%s\n", __func__);
return 0; //執行成功返回0,執行失敗返回負值
}
//調用關係:應用程序close->...->led_close
static int led_close(struct inode *inode, struct file *file)
{
int i;
for(i = 0; i < ARRAY_SIZE(led_info); i++)
gpio_set_value(led_info[i].gpio, 0);
printk("%s\n", __func__);
return 0; //執行成功返回0,執行失敗返回負值
}
//調用關係:應用程序read->...->led_read
static ssize_t led_read(struct file *file,
char __user *buf,
size_t count,
loff_t *ppos)
{
//定義初始化內核緩衝區(存儲空間再後1G虛擬內存中)
int kdata = 0x5555;
//將內核數據上報給用戶
//切記:buf雖然保存的用戶緩衝區的首地址,但不能直接訪問
//*(int *)buf = kdata;錯誤
copy_to_user(buf, &kdata, sizeof(kdata));
printk("%s\n", __func__);
return sizeof(kdata); //失敗返回負值,成功返回實際讀取的字節數
}
//調用關係:應用程序write->...->最終調用led_write
static ssize_t led_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int i;
//定義內核緩衝區
int kdata;
//拷貝用戶數據到內核
copy_from_user(&kdata, buf, sizeof(kdata));
//開或者關燈
for (i = 0; i < ARRAY_SIZE(led_info); i++)
gpio_set_value(led_info[i].gpio, kdata);
return count; //失敗返回負值,成功返回寫入的字節數
}
//定義初始化硬件操作方法
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open, //打開設備
.release = led_close, //關閉設備
.read = led_read, //讀取設備
.write = led_write //寫設備
};
static int led_init(void)
{
int i;
//申請設備號
alloc_chrdev_region(&dev, 0, 1, "tarena");
//初始化字符設備對象
cdev_init(&led_cdev, &led_fops);
//註冊字符設備對象到內核
cdev_add(&led_cdev, dev, 1);
//申請GPIO資源和配置GPIO爲輸出口,輸出0(省電)
for (i = 0; i < ARRAY_SIZE(led_info); i++) {
gpio_request(led_info[i].gpio, led_info[i].name);
gpio_direction_output(led_info[i].gpio, 0);
}
return 0;
}
static void led_exit(void)
{
int i;
//輸出0,釋放GPIO資源
for (i = 0; i < ARRAY_SIZE(led_info); i++) {
gpio_set_value(led_info[i].gpio, 0);
gpio_free(led_info[i].gpio);
}
//卸載字符設備對象
cdev_del(&led_cdev);
//釋放設備號
unregister_chrdev_region(dev, 1);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd;
int udata; //定義用戶緩衝區
//打開設備
//open->....->調用led_open
fd = open("/dev/myled", O_RDWR);
if (fd < 0) {
printf("打開設備失敗!\n");
return -1;
}
//write->...->調用led_write
while (1) {
udata = 1; //開
write(fd, &udata, sizeof(udata));
sleep(1);
udata = 0; //關
write(fd, &udata, sizeof(udata));
sleep(1);
}
//關閉設備
//close->...->調用led_close
close(fd);
return 0;
}
案例:用戶能夠指定其中某個燈的開關狀態;
提示:
用戶不僅僅要告訴燈的開關狀態,還要告訴驅動用戶現在要想操作哪個燈;
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h> //struct file_operations
#include <linux/cdev.h> //struct cdev + 設備號
#include <asm/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/uaccess.h> //copy_to_user
//聲明LED操作的數據結構
struct led_cmd {
int index;
int cmd;
};
//聲明描述LED硬件相關的數據結構
struct led_resource {
char *name;
int gpio;
};
//定義初始化LED硬件信息
static struct led_resource led_info[] = {
[0] = {
.name = "LED1",
.gpio = S5PV210_GPC0(3)
},
[1] = {
.name = "LED2",
.gpio = S5PV210_GPC0(4)
}
};
//定義設備號
static dev_t dev;
//定義字符設備對象
static struct cdev led_cdev;
//調用關係:應用程序open->....->led_open
static int led_open(struct inode *inode, struct file *file)
{
int i;
for(i = 0; i < ARRAY_SIZE(led_info); i++)
gpio_set_value(led_info[i].gpio, 1);
printk("%s\n", __func__);
return 0; //執行成功返回0,執行失敗返回負值
}
//調用關係:應用程序close->...->led_close
static int led_close(struct inode *inode, struct file *file)
{
int i;
for(i = 0; i < ARRAY_SIZE(led_info); i++)
gpio_set_value(led_info[i].gpio, 0);
printk("%s\n", __func__);
return 0; //執行成功返回0,執行失敗返回負值
}
//調用關係:應用程序read->...->led_read
static ssize_t led_read(struct file *file,
char __user *buf,
size_t count,
loff_t *ppos)
{
//定義初始化內核緩衝區(存儲空間再後1G虛擬內存中)
int kdata = 0x5555;
//將內核數據上報給用戶
//切記:buf雖然保存的用戶緩衝區的首地址,但不能直接訪問
//*(int *)buf = kdata;錯誤
copy_to_user(buf, &kdata, sizeof(kdata));
printk("%s\n", __func__);
return sizeof(kdata); //失敗返回負值,成功返回實際讀取的字節數
}
//調用關係:應用程序write->...->最終調用led_write
static ssize_t led_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
//定義內核緩衝區
struct led_cmd kdata;
//拷貝用戶數據到內核
copy_from_user(&kdata, buf, sizeof(kdata));
//開或者關燈
gpio_set_value(led_info[kdata.index - 1].gpio, kdata.cmd);
printk("%s:第%d燈被%s\n",
__func__, kdata.index, kdata.cmd?"打開":"關閉");
return count; //失敗返回負值,成功返回寫入的字節數
}
//定義初始化硬件操作方法
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open, //打開設備
.release = led_close, //關閉設備
.read = led_read, //讀取設備
.write = led_write //寫設備
};
static int led_init(void)
{
int i;
//申請設備號
alloc_chrdev_region(&dev, 0, 1, "tarena");
//初始化字符設備對象
cdev_init(&led_cdev, &led_fops);
//註冊字符設備對象到內核
cdev_add(&led_cdev, dev, 1);
//申請GPIO資源和配置GPIO爲輸出口,輸出0(省電)
for (i = 0; i < ARRAY_SIZE(led_info); i++) {
gpio_request(led_info[i].gpio, led_info[i].name);
gpio_direction_output(led_info[i].gpio, 0);
}
return 0;
}
static void led_exit(void)
{
int i;
//輸出0,釋放GPIO資源
for (i = 0; i < ARRAY_SIZE(led_info); i++) {
gpio_set_value(led_info[i].gpio, 0);
gpio_free(led_info[i].gpio);
}
//卸載字符設備對象
cdev_del(&led_cdev);
//釋放設備號
unregister_chrdev_region(dev, 1);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//聲明LED操作的數據結構
struct led_cmd {
int index; //指定燈的編號
int cmd; //開關命令
};
int main(void)
{
int fd;
struct led_cmd udata; //定義用戶緩衝區
//打開設備
//open->....->調用led_open
fd = open("/dev/myled", O_RDWR);
if (fd < 0) {
printf("打開設備失敗!\n");
return -1;
}
//write->...->調用led_write
while (1) {
udata.index = 1; //第一個燈
udata.cmd = 1; //開
write(fd, &udata, sizeof(udata));
sleep(1);
udata.index = 2; //第二個燈
write(fd, &udata, sizeof(udata));
sleep(1);
}
//關閉設備
//close->...->調用led_close
close(fd);
return 0;
}