记smarthome驱动,包括测试程序

来源:互联网 发布:blingbelle 知乎 编辑:程序博客网 时间:2024/06/08 13:30

//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 majorstatic 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 debugreturn 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)/buildOBJ := smarthome.oall:make -C $(KERN_DIR) M=`pwd` modules clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderobj-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/devices250 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/logroot@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  //0Oct 15 01:31:58 ubuntu kernel: [27769.446694] write OFF data  //1Oct 15 01:31:58 ubuntu kernel: [27769.446696] write OFF data  //2Oct 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.cwrite:内核实现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;}


//  linux/include/asm-arm/unistd.h
//系统调用确实这样子调用,这些函数都是有固定位置的
#define __NR_restart_syscall(__NR_SYSCALL_BASE+  0)
#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 


//



0 0
原创粉丝点击