[hisilicon][hi3536][linux-3.10]【PSAM卡】m50x i2c driver

m50x_device.c

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/interrupt.h>
#include<linux/i2c.h>

static struct i2c_client *m50x_client;
static int i2c_bus_num=1;


static struct i2c_board_info m50x_boardinfo=
{
    I2C_BOARD_INFO("m50x",0x50),
    .platform_data = &i2c_bus_num,
};

static int __init  m50x_dev_init(void)
{
    struct i2c_adapter  *adapter;
    int ret=0;

    //get i2c adapter
    adapter = i2c_get_adapter(*((int *)m50x_boardinfo.platform_data));
    if(NULL == adapter)
    {
        printk("failed to get adapter\n");
        return -ENODEV;
    }

    //register i2c device
    m50x_client = i2c_new_device(adapter,&m50x_boardinfo);
     if(NULL == m50x_client)
    {
        printk("failed to register i2c device\n\n");
        ret = -EINVAL;
        goto err_add_device;
    } 
    m50x_client->adapter=adapter;
    return 0;
err_add_device:
    i2c_put_adapter(adapter);
    return ret;
}


static void __exit m50x_dev_exit(void)
{
    //unregister i2c device
    i2c_unregister_device(m50x_client);
    //release i2c adapater
    i2c_put_adapter(m50x_client->adapter);
}


module_init(m50x_dev_init);
module_exit(m50x_dev_exit);

MODULE_AUTHOR("TOMMY");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PERSONAL");
 

m50x_driver.c

/****************
*******author:tommy
*******time:2018-11-25
*******description:此驱动是某公司
*******的PSAM卡的I2C驱动
************************/

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/i2c.h>
#include<linux/interrupt.h>
#include<linux/bcd.h>
#include<linux/workqueue.h>
#include<linux/slab.h>
#include<linux/types.h>
#include<linux/device.h>


#define BUF_SIZE   256
static dev_t dev_id;            //设备号
static struct class *card_class;//设备类

struct m50x
{
    struct cdev cdev;            //字符设备
    struct i2c_client *client;    //I2C外设结构体,从机
    struct semaphore sem;        //并发控制用的信号量
    wait_queue_head_t r_wq;        //读等待队列
    wait_queue_head_t w_wq;        //写等待队列
    unsigned char buf[BUF_SIZE];//缓冲区
    unsigned char flag;           //读写标志
};

static ssize_t m50x_read(struct file *file,char _user *buf,size_t count,loff_t *ofs)
{
    struct m50x *chip = file->private_data;//通过私有数据获得
    int ret=0;
    
    if(down_interruptible(&chip->sem))//若返回值非0,获取信号量失败,说明临界资源已经被占用
    {
        return -EAGAIN;
    }
    if(!chip->flag)//若不可读
    {
        up(&chip->sem);//释放信号量
        //应用空间若以非阻塞方式访问,则返回错误
        if(file->f_flags & NONBLOCK)
        {
            return -EAGAIN;    
        }
        else//若以阻塞方式访问内核空间
        {
            //将读等待队列挂起,进入睡眠
            if(wait_event_interruptible(chip->r_wq,chip->flag))
            {
                return -ERESTARTSYS;    
            }
            //若获取信号量失败
            if(down_interruptible(&chip->sem))
            {
                return -ERESTARTSYS;    
            }
        }    
    }
    
    if(count > BUF_SIZE)
        count=BUF_SIZE;
    //ret为I2C主控器接收从机的数据长度(字节数)    
    ret=i2c_master_recv(chip->client,chip->buf,count);
    if(ret > 0)
    {
        //若拷贝数据到用户空间失败
        if(copy_to_user(buf,chip->buf,count))
        {
            up(&chip->sem);//释放信号量
            return -EFAULT;    
        }    
        else
        {
            chip->flag=0;//置为可写标志
            wake_up_interruptible(&chip->w_wq);//唤醒写等待队列    
        }
    }
    
    up(&chip->sem);
    return ret ? count : -1;
}

static int m50x_write(struct file *file,char _user *buf,size_t count,loff_t *ops)
{
    struct m50x *chip=file->private_data;
    int ret=0;
    
    if(down_interruptible(&chip->sem))
    {
        return -ERESTARTSYS;    
    }
    
    if(chip->flag)//不可写
    {
        up(&chip->sem);//释放信号量
        if(file->f_flags & NONBLOCK)
        {
            return -EAGAIN;
        }
        else
        {
            //让写等待队列进入睡眠
            if(wait_event_interruptible(chip->w_wq,chip->flag == 0))
            {
                return -ERESTARTSYS;    
            }    
            if(down_interruptible(&chip->sem))//获取信号量
            {
                return -ERESTARTSYS;    
            }
        }    
    }
    
    if(count > BUF_SIZE)
    {
        count = BUF_SIZE;    
    }
    
    if(copy_from_user(chip->buf,buf,count))
    {
        up(&chip->sem);
        return -EFAULT;
    }
    else
    {
        //ret为I2C主控器向从机发送的数据长度(字节数)
        ret=i2c_master_send(chip->client,chip->buf,count);
        if(ret)
        {
            chip->flag=1;//可读    
            wake_up_interruptible(&chip->r_wq);
        }    
    }
    up(&chip->sem);
    
    return ret ? count:-1
    
}

static unsigned int m50x_poll(struct file *file,struct poll_table_struct *wait)
{
    struct m50x *chip;
    unsigned int mask =0;
    int ret=0;
    
    if(down_interruptible(&chip->sem))
    {
        return -ERESTERSYS;    
    }
    
    poll_wait(file,&chip->r_wq,wait);
    poll_wait(file,&chip->w_wq,wait);
    
    if(chip->flag)//可读
    {
        mask |=POLLIN | POLLRDNORM;    
    }
    else//可写
    {
        mask |= POLLOUT | POLLWRNORM;    
    }
    up(&chip->sem);
    
    return mask;
}

static int m50x_open(struct inode *inode,struct file *file)
{
    struct m50x *chip = container_of(inode->i_cdev,struct m50x,cdev);
    
    file->private_data = chip;
    //初始化等待队列
    init_waitqueue_head(&chip->r_wq);
    init_waitqueue_head(&chip->w_wq);
    
    memset(chip->buf,0,BUF_SIZE);
    chip->flag=0;
    
    return 0;
}

static int m50x_release(struct inode *inode,struct file *file)
{
    return 0;
}

static const struct file_operations m50x_fops=
    .owner    =    THIS_MODULE,
    .read    =    m50x_read,
    .write    =    m50x_write,
    .poll    =    m50x_poll,
    .release=    m50x_release,    
};


static int m50x_probe(struct  i2c_client *client,const struct i2c_device_id *id)
{
    struct m50x *chip;
    int ret=0;
    //申请设备结构体的动态内存空间
    chip=kzalloc(sizeof(struct m50x),GFP_KERNEL);
    if(!chip)
    {
        return -ENOMH;
        goto alloc_err;    
    }
    
    //把m50x结构体数据作为I2C从设备的私有数据,便于以后调用
    i2c_set_clientdata(client,chip);
    
    //对chip结构体变量的成员进行初始化
    chip->client = client;
    sema_init(&chip->sem,1);//初始化信号量,必须在注册前初始化,以免产生竞态
    
    //动态申请设备号,让内核自动分配设备号
    ret = alloc_chrdev_region(&dev_id,0,1,DEVICE_NAME);
    if(ret)
    {
        printk("allocate dev id failed!\n ");
        goto alloc_dev_id_err;    
    }
    //字符设备初始化,将字符设备成员与函数操作集进行绑定
    cdev_init(&chip->cdev,&m50x_fops);
    chip->cdev.owner=THIS_MODULE;
    //完成设备注册,将设备与设备号绑定
    ret=cdev_add(&chip->cdev,dev_id,1);
    if(ret)
    {
        printk("cdev add err!\n");
        goto cdev_add_err;    
    }
    //创建一个类,存放在sysfs目录下,sysfs/class/
    card_class=class_create(THIS_MODULE,DEVICE_NAME);
    if(NULL==card_class)
    {
        printk("class create failed!\n");
        ret=-EBUSY;
        goto class_err;    
    }
    //创建设备节点成功,存放在/dev/目录下
    device_create(card_class,NULL,dev_id,NULL,DEVICE_NAME);
    printk(DEVICE_NAME" initalized!\n");
    
    return(0);
    
alloc_err:
    return(ret);
alloc_dev_id_err:
    kfree(chip);
cdev_add_err:
    unregister_chrdev_region(dev_id,1);
class_err:
    cdev_del(&chip->cdev);
        
}

static int m50x_remove(struct i2c_client *client,const struct i2c_device_id *id)
{
    struct m50x *chip = i2c_get_clientdata(client);    //获得外设的数据
    device_destroy(card_class,dev_id);                //将设备号从设备类中删除
    class_destroy(card_class);                        //删除设备类
    cdev_del(&chip->cdev);                            //删除字符设备
    unregister_chrdev_region(dev_id,1);                //注销设备号
    kfree(chip);                                    //释放动态内存
    printk(DEVICE_NAME" destroyed!");
    return 0;    
}

static const struct i2c_device_id m50x_id[]=
{
    {"m50x",0},    
    {}
};
MODULE_DEVICE_TABLE(i2c,m50x_id);


static struct i2c_driver   m50x_driver=
{
    .driver=
    {
        .name = "m50x_card",
        .owner=THIS_MODULE,

    },
    .probe=m50x_probe,
    .remove=m50x_remove,
    .id_table=m50x_id,

};
//入口函数
static __init  int m50x_drv_init(void)
{

    return i2c_add_driver(&m50x_driver);
}
//出口函数
static __exit  void m50x_drv_exit(void)
{

    i2c_del_driver(&m50x_driver);
}


module_init(m50x_drv_init);
module_init(m50x_drv_exit);

MODULE_AUTHOR("TOMMY");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LINUX_WORKER");

makefile


obj-o += m50x

PWD    :=    $(shell pwd)
KERDIR := ${PWD}/../

    make -C  ${KERDIR}  -M ${PWD}  
    
    
    RKImageMaker.exe -RK330A Image\MiniLoaderAll.bin  D:\Image\update_old.img D:\Image\update_%FILENAME% -os_type:androidos

猜你喜欢

转载自blog.csdn.net/u014689845/article/details/88654244