//Environment : ubuntu 10.04
//tool : gcc
//platform : i386 machine
//smarthome.c
// ====================================================#include<linux/module.h>
#include<linux/init.h>
#include<linux/types.h>
#include<linux/fs.h>
#include<linux/seq_file.h>
#include<linux/proc_fs.h>
#include<linux/errno.h>
#include<linux/mm.h>
#include<linux/sched.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<asm/io.h>
#include<asm/system.h>
#include<asm/uaccess.h>
#define DEV_ON 1
#define DEV_OFF 0
#define LIGHTNUM 6
#define TVNUM 1
#define AIRCONDNUM 4
#define HUGNUM 10
#define DEV_SUM LIGHTNUM + TVNUM + AIRCONDNUM + HUGNUM
#define MEM_CLEAR 0x01
#define SMARHOMEDEV_MAJOR 0//using dynamic major
static int SmartHomeDevice_major = SMARHOMEDEV_MAJOR;
int SmartHomeDevice_nr_devs =1;
typedef struct _tagSmartHomeDevice{
struct cdev cdev;
char lightsSwitch[LIGHTNUM];
char TVSwitch[TVNUM];
char AirCondSwitch[AIRCONDNUM];
char Hug[HUGNUM];
}SmartHomeDevice;
SmartHomeDevice *pSmartHomeDev;
static loff_t SmartHomeDevice_llseek(struct file *filp,loff_t offset,int orig);
static void SmartHomeDevice_setup_cdev(SmartHomeDevice *dev,int index);
static ssize_t SmartHomeDevice_read(struct file * flip, char __user* buf,size_t count,loff_t *ppos);
static ssize_t SmartHomeDevice_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos);
static int SmartHomeDevice_ioctl(struct inode *inodep, struct file *filp,unsigned int cmd, unsigned long arg);
static int SmartHomeDevice_open(struct inode *inode, struct file *filp);
static const struct file_operations SmartHomeDevice_fops = {
.owner = THIS_MODULE,
.open = SmartHomeDevice_open,
.llseek = SmartHomeDevice_llseek,
.read = SmartHomeDevice_read,
.write = SmartHomeDevice_write,
.ioctl = SmartHomeDevice_ioctl
};
static int SmartHomeDevice_open(struct inode *inode, struct file *filp){
filp->private_data = pSmartHomeDev;
return 0;
}
static int SmartHomeDevice_ioctl(struct inode *inodep, struct file *filp,unsigned int cmd, unsigned long arg){
//SmartHomeDevice *dev = filp->private_data;
switch(cmd){
case MEM_CLEAR:
//memset(dev->mem,0,GLOBALMEM_SIZE);
printk(KERN_INFO "SmartHomeDevice is set to zero \n");
break;
default: return -EINVAL;
}
return 0;
}
static void SmartHomeDevice_setup_cdev(SmartHomeDevice* dev , int index){
int err,devno=MKDEV(SmartHomeDevice_major,index);
cdev_init(&dev->cdev,&SmartHomeDevice_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops= &SmartHomeDevice_fops;
err= cdev_add(&dev->cdev,devno,1);
if(err)
printk(KERN_NOTICE "ERROR %d adding SmartHomeDevice",err);
}
static ssize_t SmartHomeDevice_read(struct file * filp, char __user* buf,size_t count,loff_t *ppos){
unsigned long p = *ppos;
int ret =0;
SmartHomeDevice *dev = filp->private_data;
if(p >= DEV_SUM) return count? -ENXIO : 0;
if(p >= DEV_SUM- p){
count = DEV_SUM-p;
}
if(copy_to_user(buf,(void*)(dev->lightsSwitch+ p),count)){
ret= -EFAULT;
}
else{
*ppos += count;
ret = count;
}
return ret;
}
static ssize_t SmartHomeDevice_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos){
unsigned long p = *ppos;
char* pChar= 0;
int ret = 0 ,i =0;
SmartHomeDevice *dev = filp->private_data;
if(dev == NULL){
return 0;
}
if(p >= DEV_SUM){
return count? -ENXIO :0;
}
if(count > DEV_SUM - p){
count = DEV_SUM - p;
}
if(copy_from_user(dev->lightsSwitch + p,buf,count)){
ret = -EFAULT;
}
else{
*ppos =+ count;
ret = count;
printk(KERN_INFO "written %d bytes from %ld \n", count ,p);
pChar = dev->lightsSwitch;
for(i=0;((i<count) && (pChar != NULL));i++,pChar++)
{
switch(*pChar){
case DEV_OFF: printk("write OFF data \n");break;
case DEV_ON: printk("write ON data \n");break;
default: printk("write error data \n"); break;
}
}
}
return ret;
}
static loff_t SmartHomeDevice_llseek(struct file *filp,loff_t offset,int orig)
{
loff_t ret = 0;
switch(orig)
{
case 0:
if(offset <0 ){
ret = -EINVAL;
break;
}
if((unsigned int )offset > DEV_SUM){
ret = - EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1:
if((filp->f_pos+ offset) > DEV_SUM){
ret = -EINVAL;
break;
}
if((filp->f_pos+ offset)<0)
{
ret = -EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
int SmartHomeDevice_read_procmem(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
{
//int i, j, len = 0;
//int limit = count - 80; /* Don't print more than this */
//struct SmartHomeDevice_dev *d = pSmartHomeDev;
//*eof = 1;
return 0;
}
/*
* Here are our sequence iteration methods. Our "position" is
* simply the device number.
*/
static void *SmartHomeDevice_seq_start(struct seq_file *s, loff_t *pos)
{
if (*pos >= SmartHomeDevice_nr_devs)
return NULL; /* No more to read */
return pSmartHomeDev + *pos;
}
static void *SmartHomeDevice_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
if (*pos >= SmartHomeDevice_nr_devs)
return NULL;
return pSmartHomeDev + *pos;
}
static void SmartHomeDevice_seq_stop(struct seq_file *s, void *v)
{
/* Actually, there's nothing to do here */
}
static int SmartHomeDevice_seq_show(struct seq_file *s, void *v)
{
//struct SmartHomeDevice_dev *dev = (struct SmartHomeDevice_dev *) v;
//struct SmartHomeDevice_qset *d;
//int i;
//seq_printf(s, "\nDevice %i: qset %i, q %i, sz %li\n",
// 1000, 800,
// 120, 110);
return 0;
}
/*
* Tie the sequence operators up.
*/
static struct seq_operations SmartHomeDevice_seq_ops = {
.start = SmartHomeDevice_seq_start,
.next = SmartHomeDevice_seq_next,
.stop = SmartHomeDevice_seq_stop,
.show = SmartHomeDevice_seq_show
};
/*
* Now to implement the /proc file we need only make an open
* method which sets up the sequence operators.
*/
static int SmartHomeDevice_proc_open(struct inode *inode, struct file *file)
{
return seq_open(file, &SmartHomeDevice_seq_ops);
}
/*
* Create a set of file operations for our proc file.
*/
static struct file_operations SmartHomeDevice_proc_ops = {
.owner = THIS_MODULE,
.open = SmartHomeDevice_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
//===========Create proc file ===========
static void SmartHomeDevice_create_proc(void)
{
struct proc_dir_entry *entry;
entry = create_proc_read_entry("SmartHomeDevice", 0 /* default mode */,
NULL /* parent dir */, SmartHomeDevice_read_procmem,
NULL /* client data */);
if (entry)
entry->proc_fops = &SmartHomeDevice_proc_ops;
}
//================================
int SmartHomeDevice_init(void){
int result;
dev_t devno = MKDEV(SmartHomeDevice_major,0);
if(SmartHomeDevice_major){
result = register_chrdev_region(devno,1,"smarthome");
}
else{
result = alloc_chrdev_region(&devno,0,1,"smarthome");
SmartHomeDevice_major = MAJOR(devno);
}
if(result<0){
return result;
}
pSmartHomeDev = kmalloc(sizeof(SmartHomeDevice), GFP_KERNEL);
if (!pSmartHomeDev){
result = -ENOMEM;
goto fail_malloc;
}
memset(pSmartHomeDev,0,sizeof(SmartHomeDevice));
SmartHomeDevice_setup_cdev(pSmartHomeDev,0);
//SmartHomeDevice_create_proc(); //we don't need proc debug
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return result;
}
void SmartHomeDevice_exit(void){
cdev_del(&pSmartHomeDev->cdev);
kfree(pSmartHomeDev);
unregister_chrdev_region(MKDEV(SmartHomeDevice_major,0),1);
}
MODULE_AUTHOR("NOBODY");
MODULE_LICENSE("GPL");
module_param(SmartHomeDevice_major,int,S_IRUGO);
module_init(SmartHomeDevice_init);
module_exit(SmartHomeDevice_exit);
//================makefile=========================
KERN_DIR := /lib/modules/$(shell uname -r)/build
OBJ := smarthome.o
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += $(OBJ)
//======================testFile.c==================
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#define LIGHTNUM 6
#define TVNUM 1
#define AIRCONDNUM 4
#define HUGNUM 10
#define DEV_SUM LIGHTNUM + TVNUM + AIRCONDNUM + HUGNUM
/*
* testfile <dev> <on|off>
*/
void print_usage(char *file)
{
printf("Usage:\n");
printf("%s <dev> <device nmae> <number> <on|off>\n",file);
printf("device name is :light/TV/air-condtion/hug");
printf("eg. \n");
printf("%s /dev/smarthome light 0 on\n", file);
printf("%s /dev/smarthome TV 0 on\n", file);
printf("%s /dev/smarthome air-condition 0 on\n", file);
printf("%s /dev/smarthome hug 0 on\n", file);
}
int main(int argc, char **argv)
{
int fd;
char* filename;
char lightsSwitch[LIGHTNUM]={0,0,0,0,0,0};
char TVSwitch[TVNUM]={0};
char AirCondSwitch[AIRCONDNUM]={0,0,0,0};
char HugSwitch[HUGNUM]={0,0,0,0,0,0};
int num,i;
if (argc != 5)
{
print_usage(argv[0]);
return 0;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if (fd < 0)
{
printf("error, can't open %s\n", filename);
return 0;
}
if (!strcmp("light", argv[2]))
{
num=atoi(argv[3]);
if(!strcmp("on", argv[4])){
lightsSwitch[num] = 1;
}else lightsSwitch[num] = 0;
for(i=0;i<LIGHTNUM;i++){
printf("lightswtich[%d] = %d \n " , i , lightsSwitch[i]);
}
write(fd, lightsSwitch, LIGHTNUM,0);
}
else if (!strcmp("TV", argv[2]))
{
num=atoi(argv[3]);
if(!strcmp("on", argv[4])){
TVSwitch[num] = 1;
}else TVSwitch[num] = 0;
write(fd, TVSwitch, TVNUM,LIGHTNUM);
}
else if (!strcmp("air-condition", argv[2]))
{
num=atoi(argv[3]);
if(!strcmp("on", argv[4])){
AirCondSwitch[num] = 1;
}else AirCondSwitch[num] = 0;
write(fd, AirCondSwitch, AIRCONDNUM,TVNUM+LIGHTNUM);
}
else if (!strcmp("hug", argv[2]))
{
num=atoi(argv[3]);
if(!strcmp("on", argv[4])){
HugSwitch[num] = 1;
}else HugSwitch[num] = 0;
write(fd, HugSwitch, AIRCONDNUM,TVNUM+LIGHTNUM+AIRCONDNUM);
}
return 0;
}
//====================编译驱动文件============================
$ make
$ sudo insmod smarthome.ko
$ cat /proc/devices
250 smarthome
//创建 dev 节点
$ sudo mknod /dev/smarthome c 250 0
//=====================编译testFile.c============================
$ gcc -c testFile.c
$ gcc -o testFile testFile.o
//============================运行测试程序======================
//使用root权限运行
$ sudo ./testFile /dev/smarthome air-condition 3 on
//==============查看打印信息===================================
/使用root权限
$ cd /var/log
root@ubuntu:/var/log# echo > kern.log
root@ubuntu:/var/log# cat kern.log
Oct 15 01:31:58 ubuntu kernel: [27769.446685] written 4 bytes from 0
Oct 15 01:31:58 ubuntu kernel: [27769.446692] write OFF data //0
Oct 15 01:31:58 ubuntu kernel: [27769.446694] write OFF data //1
Oct 15 01:31:58 ubuntu kernel: [27769.446696] write OFF data //2
Oct 15 01:31:58 ubuntu kernel: [27769.446697] write ON data // 3 this is on
PS:BUG:我们看到写入的位置都是0开始的,所以write的最后一个参数根本没用,写不了指定的位置,于是查看write的原型:
定义函数
ssize_t write (int fd,const void * buf,size_t count);
ssize_t write (int fd,const void * buf,size_t count);
故第四个参数根本没用,在查询,发现还有pwrite的这个函数:原型
ssize_t pwrite(unsigned int fd, const char __user *buf,size_t count, loff_t pos)
我编译出来的ARM版本的是这样子的:
// linux/fs/read_write.c
write:内核实现
asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count)
{
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
file = fget_light(fd, &fput_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_write(file, buf, count, &pos);
file_pos_write(file, pos);
fput_light(file, fput_needed);
}
return ret;
}
pwrite:内核实现
asmlinkage ssize_t sys_pwrite64(unsigned int fd, const char __user *buf,
size_t count, loff_t pos)
{
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
if (pos < 0)
return -EINVAL;
file = fget_light(fd, &fput_needed);
if (file) {
ret = -ESPIPE;
if (file->f_mode & FMODE_PWRITE)
ret = vfs_write(file, buf, count, &pos);
fput_light(file, fput_needed);
}
return ret;
}
#define __NR_exit (__NR_SYSCALL_BASE+ 1)
#define __NR_fork (__NR_SYSCALL_BASE+ 2)
#define __NR_read (__NR_SYSCALL_BASE+ 3)
#define __NR_write (__NR_SYSCALL_BASE+ 4)
#define __NR_open (__NR_SYSCALL_BASE+ 5)
#define __NR_close (__NR_SYSCALL_BASE+ 6)
具体不做讨论!
可以看到pwrite是带文件位置的访问,所以只要把测试代码中的write改为pwrite即可指定四个参数的文件对应位置写入数据了!
编译测试:
root@ubuntu:/home/liqinghan/workspace/smartHome# ./testFile /dev/smarthome air-condition 2 on
root@ubuntu:/home/liqinghan/workspace/smartHome# tail /var/log/kern.log
Oct 15 20:22:21 ubuntu kernel: [ 6881.225695] written 4 bytes from 7
Oct 15 20:22:21 ubuntu kernel: [ 6881.225701] write OFF data
Oct 15 20:22:21 ubuntu kernel: [ 6881.225704] write OFF data
Oct 15 20:22:21 ubuntu kernel: [ 6881.225705] write ON data
Oct 15 20:22:21 ubuntu kernel: [ 6881.225707] write OFF data
//