自旋锁/互斥锁 设备树 iic驱动总线 day66 67 68

十二:锁/互斥/信号量/自旋

锁持有时间

长-------互斥锁

短-------自旋锁

自旋锁不会使线程状态发生切换

一直处于用户态,即线程---直都是运行的;

不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。

1.自旋锁

c 复制代码
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <mach/gpio-nrs.h>
#include <mach/gpio.h>
#include <linux/miscdevice.h>
#include <asm/ioctl.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>

#define DEV_NAME "adc4"
static wait_queue_head_t wq;
static int condition;
static struct tasklet_struct task;
spinlock_t lock;

static void task_func(unsigned long arg)
{
	spin_lock(&lock);
	condition = 1;
	spin_unlock(&lock);

	wake_up_interruptible(&wq);
	printk("task_fun arg = %ld\n",arg);
}

static irqreturn_t eint8_handler(int irq_num,void * dev)
{
	unsigned int arg = *(unsigned int *)dev;
	if(100 != arg)
	{
		return IRQ_NONE;
	}

	spin_lock(&lock);
	condition = 1;
	spin_unlock(&lock);

	tasklet_schedule(&task);
	printk("irq_num = %d	dev = %d\n",irq_num,arg);
	return IRQ_HANDLED;
}

static void key2_init(void)
{
}

static void key2_deinit(void)
{
}

static int open(struct inode * node, struct file * file)
{
	spin_lock_init(&lock);
	key2_init();
	printk("key4  open ...\n");
	return 0;
}

static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{
	//copy_to_user();
	printk("key4 read start\n");
	spin_lock(&lock);
	condition = 0;
	spin_unlock(&lock);
	wait_event_interruptible(wq,condition);
	printk("key4 read end...\n");
	return 0;
}

static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
	printk("key4 write ...\n");
	return 0;
}

static int close(struct inode * node, struct file * file)
{
	key2_deinit();
	printk("key4 close ...\n");
	return 0;
}

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.release = close
};

static struct miscdevice misc = 
{
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEV_NAME,
	.fops = &fops
};

static unsigned int arg = 100;

static int __init key1_init(void)
{
	int ret = misc_register(&misc);
	if(ret < 0)
		goto err_misc_register;
	ret = request_irq(IRQ_EINT8, eint8_handler, IRQF_TRIGGER_FALLING, "irq_eint8", &arg);
	if(ret <0)
		goto err_request_irq;

	init_waitqueue_head(&wq);
	tasklet_init(&task,task_func,200);

	printk("key4_init   ....\n");
	return ret;

err_request_irq:
	disable_irq(IRQ_EINT8);
	free_irq(IRQ_EINT8,&arg);
	printk("request_irq failed\n");
	return ret;

err_misc_register:
	misc_deregister(&misc);
	printk("misc_register  faikey\n");
	return ret;	
}

static void __exit key1_exit(void)
{
	disable_irq(IRQ_EINT8);
	free_irq(IRQ_EINT8,&arg);
	misc_deregister(&misc);
	printk("key4_exit   ....\n");
}

module_init(key1_init);
module_exit(key1_exit);
MODULE_LICENSE("GPL");

2.互斥锁

c 复制代码
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <mach/gpio-nrs.h>
#include <mach/gpio.h>
#include <linux/miscdevice.h>
#include <asm/ioctl.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/lockdep.h>
#include <linux/mutex.h>

#define DEV_NAME "adc4"
static wait_queue_head_t wq;
static int condition = 0;
static struct work_struct work;
static struct mutex lock;

static void work_func(struct work_struct *work)
{
	ssleep(1);
	mutex_lock(&lock);
	condition = 1;
	mutex_unlock(&lock);

	wake_up_interruptible(&wq);
	printk("task_func ....\n");
}

static irqreturn_t eint8_handler(int irq_num, void * dev)
{
	unsigned int arg = *(unsigned int *)dev;
	if(100 != arg)
		return IRQ_NONE;
	mutex_lock(&lock);
	schedule_work(&work);
	mutex_unlock(&lock);

	printk("irq_num  = %d  dev = %d\n", irq_num, arg);
	return IRQ_HANDLED;
}

static int open(struct inode * node, struct file * file)
{
	mutex_init(&lock);
	printk("adc4  open ...\n");
	return 0;
}

static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{
	//copy_to_user();
	printk("adc4 read start...\n");
	mutex_lock(&lock);
	condition = 0;
	mutex_unlock(&lock);

	wait_event_interruptible(wq, condition);
	printk("adc4 read end...\n");
	return 0;
}

static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
	printk("adc4 write ...\n");
	return 0;
}

static long ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{
	return 0;
}

static int close(struct inode * node, struct file * file)
{
	printk("adc4 close ...\n");
	return 0;
}

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.unlocked_ioctl = ioctl,
	.release = close
};

static struct miscdevice misc = 
{
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEV_NAME,
	.fops = &fops
};

static unsigned int arg = 100;
static int __init adc1_init(void)
{
	int ret = misc_register(&misc);
	if(ret < 0)
		goto err_misc_register;

	ret = request_irq(IRQ_EINT8, eint8_handler, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "irq_eint8", &arg);
	if(ret < 0)
		goto err_request_irq;

	init_waitqueue_head(&wq);
	INIT_WORK(&work, work_func);

	printk("adc4_init   ....\n");
	return ret;
err_request_irq:
	disable_irq(IRQ_EINT8);
	free_irq(IRQ_EINT8, &arg);
	printk("request_irq failed\n");
	return ret;

err_misc_register:
	misc_deregister(&misc);
	printk("misc_register  faiadc\n");
	return ret;	
}

static void __exit adc_exit(void)
{
	disable_irq(IRQ_EINT8);
	free_irq(IRQ_EINT8, &arg);
	misc_deregister(&misc);
	printk("adc4_exit   ....\n");
}

module_init(adc1_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");

3.信号量

c 复制代码
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <mach/gpio-nrs.h>
#include <mach/gpio.h>
#include <linux/miscdevice.h>
#include <asm/ioctl.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/lockdep.h>
#include <linux/mutex.h>
#include <linux/semaphore.h>


#define DEV_NAME "adc4"
static wait_queue_head_t wq;
static int condition = 0;
static struct work_struct work;
static struct semaphore sem;

static void work_func(struct work_struct *work)
{
	ssleep(1);
	down(&sem);
	condition = 1;
	up(&sem);

	wake_up_interruptible(&wq);
	printk("task_func ....\n");
}

static irqreturn_t eint8_handler(int irq_num, void * dev)
{
	unsigned int arg = *(unsigned int *)dev;
	if(100 != arg)
		return IRQ_NONE;

	down(&sem);
	schedule_work(&work);
	up(&sem);

	printk("irq_num  = %d  dev = %d\n", irq_num, arg);
	return IRQ_HANDLED;
}

static int open(struct inode * node, struct file * file)
{
	sema_init(&sem,1);
	printk("adc4  open ...\n");
	return 0;
}

static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{
	//copy_to_user();
	printk("adc4 read start...\n");
	down(&sem);
	condition = 0;
	up(&sem);

	wait_event_interruptible(wq, condition);
	printk("adc4 read end...\n");
	return 0;
}

static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
	printk("adc4 write ...\n");
	return 0;
}

static long ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{
	return 0;
}

static int close(struct inode * node, struct file * file)
{
	printk("adc4 close ...\n");
	return 0;
}

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.unlocked_ioctl = ioctl,
	.release = close
};

static struct miscdevice misc = 
{
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEV_NAME,
	.fops = &fops
};

static unsigned int arg = 100;
static int __init adc1_init(void)
{
	int ret = misc_register(&misc);
	if(ret < 0)
		goto err_misc_register;

	ret = request_irq(IRQ_EINT8, eint8_handler, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "irq_eint8", &arg);
	if(ret < 0)
		goto err_request_irq;

	init_waitqueue_head(&wq);
	INIT_WORK(&work, work_func);

	printk("adc4_init   ....\n");
	return ret;
err_request_irq:
	disable_irq(IRQ_EINT8);
	free_irq(IRQ_EINT8, &arg);
	printk("request_irq failed\n");
	return ret;

err_misc_register:
	misc_deregister(&misc);
	printk("misc_register  faiadc\n");
	return ret;	
}

static void __exit adc_exit(void)
{
	disable_irq(IRQ_EINT8);
	free_irq(IRQ_EINT8, &arg);
	misc_deregister(&misc);
	printk("adc4_exit   ....\n");
}

module_init(adc1_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");

十三:设备树 I.MX6U-MINI------imx6ull

1.一些基础概念

根文件系统制作:

​ busybox: 能生成系统需要的核心文件

​ 还需做以下操作才能生成可以使用的根文件系统:

​ 手动添加库文件 其他文件及修改相关配置文件

​ buildroot: 编译完即可生成可以直接使用的根文件系统

​ ---- 添加各种服务 eg: tftp nfs

​ ---- 一定要在连接互联网的环境下才能做,因为要下载使用的资源 任意写一个应用程序 `

先配置双网卡

2.设备树

c 复制代码
//vi arch/arm/boot/dts/imx6ull-alientek-emmc.dts

	puteleds {
        #address-cells = <1>;
        #size-cells = <1>;

        compatible = "pute-driver-leds";
        status = "okay";
        reg = < 0X020C406C 0X04    
                0X020E0068 0X04    
                0X020E02F4 0X04    
                0X0209C004 0X04    
                0X0209C000 0X04>;  
    };
c 复制代码
make imx6ull-alientek-emmc.dtb	//make 制定的设备树,加后缀就行


cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb ~/tftpboot/	//更新tftpboot
c 复制代码
    //查找设备树节点
    pdtsdevice = of_find_node_by_path("/puteleds");
    if (!pdtsdevice)
        pr_info("of_find_node_by_path");

    //读取属性中的compatible字符串
    ret = of_property_read_string(pdtsdevice, "compatible", &pcompatible);
    if (ret)
        pr_info("of_property_read_string failed");
    
    pr_info("compatible = %s\n", pcompatible);

    ret = of_property_read_u32_array(pdtsdevice, "reg", regaddr, 10);
    if (ret)
        pr_info("of_property_read_u32_array");

    for (i = 0; i < 10; i+=2)
    {
        pr_info("addr: %#x size:%d\n", regaddr[i], regaddr[i+1]);
    }

    //虚拟地址向物理地址映射
    pccgr1 = devm_ioremap(pdevice, regaddr[0], regaddr[1]);
    if (!pccgr1) {
        pr_info("fail to ioremap");
        goto err_device_create;
    }  
    imuxrcsw = devm_ioremap(pdevice, regaddr[2], regaddr[3]); 
    if (!imuxrcsw) {
        pr_info("fail to ioremap");
        goto err_device_create;
    }  
    imuxrcpad = devm_ioremap(pdevice, regaddr[4], regaddr[5]); 
    if (!imuxrcpad) {
        pr_info("fail to ioremap");
        goto err_device_create;
    }  
    gpiodir = devm_ioremap(pdevice, regaddr[6], regaddr[7]); 
    if (!gpiodir) {
        pr_info("fail to ioremap");
        goto err_device_create;
    }  
    gpiodat = devm_ioremap(pdevice, regaddr[8], regaddr[9]); 
    if (!gpiodat) {
        pr_info("fail to ioremap");
        goto err_device_create;
    }  

3.完整代码

c 复制代码
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm-generic/io.h>
#include <asm/uaccess.h>
#include <linux/of.h>

static ssize_t led_read(struct file *fp, char __user *puser, size_t n, loff_t *off);
static ssize_t led_write(struct file *fp, const char __user *puser, size_t n, loff_t *off);
static int led_open(struct inode *node, struct file *fp);
static int led_release(struct inode *node, struct file *fp);
extern void __iomem *devm_ioremap(struct device *dev, resource_size_t offset, resource_size_t size);
ssize_t led_show(struct device *dev, struct device_attribute *attr, char *buf);
ssize_t led_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);

static int ledstat;
static struct device *pdevice;
static struct class *pclass;
static struct cdev *pcdev;
static dev_t devno;
static void __iomem *pccgr1;
static void __iomem *imuxrcsw;
static void __iomem *imuxrcpad;
static void __iomem *gpiodir;
static void __iomem *gpiodat;
static struct device_attribute led_attr = __ATTR(ledbright, 0664, led_show, led_store);
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = led_read,
    .write = led_write,
    .open = led_open,
    .release = led_release,
};

static ssize_t led_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{   
    unsigned long ret = 0;

    ret = copy_to_user(puser, &ledstat, sizeof(ledstat));
    if (ret) 
        pr_info("copy_to_user failed\n");
    
    pr_info("led read success!\n");

    return 0;
}

static ssize_t led_write(struct file *fp, const char __user *puser, size_t n, loff_t *off)
{
    unsigned int tmpvalue = 0;
    unsigned long len = 0;

    len = copy_from_user(&ledstat, puser, sizeof(ledstat));
    if (len) {
        pr_info("copy_from_user failed");
    }

    if (1 == ledstat)
    {
        //置0开灯
        tmpvalue = readl(gpiodat);
        tmpvalue &= ~(0x1 << 3);
        writel(tmpvalue, gpiodat);
        ledstat = 0;
    }
    else if (0 == ledstat)
    {
        //置1关灯
        tmpvalue = readl(gpiodat);
        tmpvalue |= 0x1 << 3;
        writel(tmpvalue, gpiodat);
        ledstat = 0;
    }

    pr_info("led write success!\n");

    return 0;
}

static int led_open(struct inode *node, struct file *fp)
{
    pr_info("led open success!\n");

    return 0;
}

static int led_release(struct inode *node, struct file *fp)
{
    pr_info("led release success!\n");

    return 0;
}

ssize_t led_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    ssize_t len = 0;

    if (1 == ledstat)
    {
        len = sprintf(buf, "LED_ON\n");
    }
    else if (0 == ledstat)
    {
        len = sprintf(buf, "LED_OFF\n");
    }

    return len;
}

ssize_t led_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    unsigned int tmpvalue = 0;
    char tmpbuff[32] = {0};

    sscanf(buf, "%s", tmpbuff);

    if (!strcmp(tmpbuff, "LED_ON"))
    {
        //置0开灯
        tmpvalue = readl(gpiodat);
        tmpvalue &= ~(0x1 << 3);
        writel(tmpvalue, gpiodat);
        ledstat = 1;
    }
    else if (!strcmp(tmpbuff, "LED_OFF"))
    {
        //置1关灯
        tmpvalue = readl(gpiodat);
        tmpvalue |= 0x1 << 3;
        writel(tmpvalue, gpiodat);
        ledstat = 0;
    }

    return count;
}

static int __init led_init(void)
{   
    int ret = 0;
    unsigned int tmpvalue = 0;
    struct device_node *pdtsdevice = NULL;
    const char *pcompatible = NULL;
    unsigned int regaddr[10] = {0};
    int i = 0;

    //注册设备号
    ret = alloc_chrdev_region(&devno, 0, 1, "myled");
    if (ret) {
        pr_info("alloc_chrdev_region failed\n");
        goto err_alloc_chrdev_region;
    }

    pr_info("major:%d minor:%d\n", MAJOR(devno), MINOR(devno));

    //创建cdev并初始化
    pcdev = cdev_alloc();
    if (!pcdev) {
        pr_info("cdev_alloc failed\n");
        goto err_cdev_alloc;
    }

    //将cdev加入字符设备结构中
    pcdev->ops = &fops;
    ret = cdev_add(pcdev, devno, 1);
    if (ret) {
        pr_info("cdev_alloc failed\n");
        goto err_cdev_add;
    }

    //创建设备类
    pclass = class_create(THIS_MODULE, "led_class");
    if (!pclass) {
        pr_info("class_create failed\n");
        goto err_class_create;
    }  

    //创建具体设备
    pdevice = device_create(pclass, NULL, devno, NULL, "led%d", 0);
    if (!pclass) {
        pr_info("device_create failed\n");
        goto err_device_create;
    }  

    //查找设备树节点
    pdtsdevice = of_find_node_by_path("/puteleds");
    if (!pdtsdevice)
        pr_info("of_find_node_by_path");

    //读取属性中的compatible字符串
    ret = of_property_read_string(pdtsdevice, "compatible", &pcompatible);
    if (ret)
        pr_info("of_property_read_string failed");
    
    pr_info("compatible = %s\n", pcompatible);

    ret = of_property_read_u32_array(pdtsdevice, "reg", regaddr, 10);
    if (ret)
        pr_info("of_property_read_u32_array");

    for (i = 0; i < 10; i+=2)
    {
        pr_info("addr: %#x size:%d\n", regaddr[i], regaddr[i+1]);
    }

    //虚拟地址向物理地址映射
    pccgr1 = devm_ioremap(pdevice, regaddr[0], regaddr[1]);
    if (!pccgr1) {
        pr_info("fail to ioremap");
        goto err_device_create;
    }  
    imuxrcsw = devm_ioremap(pdevice, regaddr[2], regaddr[3]); 
    if (!imuxrcsw) {
        pr_info("fail to ioremap");
        goto err_device_create;
    }  
    imuxrcpad = devm_ioremap(pdevice, regaddr[4], regaddr[5]); 
    if (!imuxrcpad) {
        pr_info("fail to ioremap");
        goto err_device_create;
    }  
    gpiodir = devm_ioremap(pdevice, regaddr[6], regaddr[7]); 
    if (!gpiodir) {
        pr_info("fail to ioremap");
        goto err_device_create;
    }  
    gpiodat = devm_ioremap(pdevice, regaddr[8], regaddr[9]); 
    if (!gpiodat) {
        pr_info("fail to ioremap");
        goto err_device_create;
    }  

    //时钟
    tmpvalue = readl(pccgr1);
    tmpvalue &= ~(0x3 << 26);
    tmpvalue |= (0x3 << 26);
    writel(tmpvalue, pccgr1);

    //设置为GPIO
    writel(0x5, imuxrcsw);

    //设置电器属性
    writel(0x10B0, imuxrcpad);

    //GPIO方向
    tmpvalue = readl(gpiodir);
    tmpvalue |= 0x1 << 3;
    writel(tmpvalue, gpiodir);

    //关灯
    //置1关灯
    tmpvalue = readl(gpiodat);
    tmpvalue |= (0x1 << 3);
    writel(tmpvalue, gpiodat);

    //增加sysfs文件系统中的调试节点
    ret = device_create_file(pdevice, &led_attr);
    if (ret) 
        pr_info("device_create_file failed");

    pr_info("led init success\n");

    return 0;
err_device_create:
    class_destroy(pclass); 
err_class_create:
    cdev_del(pcdev);
err_cdev_add:
err_cdev_alloc:
    unregister_chrdev_region(devno, 1);
err_alloc_chrdev_region:
    return -1;
}

static void __exit led_exit(void)
{
    device_destroy(pclass, devno);
    class_destroy(pclass);
    cdev_del(pcdev);
    unregister_chrdev_region(devno, 1);
    pr_info("led exit success\n");

    return;
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("haiersen");

4.关于设备树一个Makefile搞定

c 复制代码
make -f Makefile1
makefile 复制代码
//vi Makefile1


modulename=led

all:
	make -C $(modulename)_app_96
	make -C $(modulename)_drv_96
makefile 复制代码
//vi led_drv_96/Makefile


#模块名
modulename=led_drv

#内核路径
kerdir=/home/linux/ARM/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

#当前路径
curpath=$(shell pwd)

#将代码加入模块编译选项中
obj-m+=$(modulename).o 

all:
	make -C $(kerdir) modules M=$(curpath)
	cp $(modulename).ko ~/nfs/rootfs_os/
.PHONY:
clean:
	rm $(modulename).ko
makefile 复制代码
// vi led_app_96/Makefile

#模块名称
appname=led_app1

#编译器
CC=arm-linux-gnueabihf-gcc

$(appname):
	$(CC) main.c -o $@
	cp $(appname) ~/nfs/rootfs_os/ 
.PHONY:
clean:
	rm $(appname)
1.关于设备树文件
c 复制代码
static int __init led_init(void)
{
    int ret = 0;
    
    ret = misc_register(&misc_device);
    if (ret)
        pr_info("misc register led failed\n");

    pdtsnode = of_find_node_by_path("/puteleds");	//找对应位置
    if (!pdtsnode)
        pr_info("of_find_node_by_path failed\n");

    ledgpionum = of_get_named_gpio(pdtsnode, "led-gpio", 0);	//匹配led-gpio = <&gpio1 3 0>;
    if (ledgpionum < 0)
        pr_info("of_get_named_gpio failed\n");

    ret = gpio_request(ledgpionum, "puteled");		//
    if (ret)
        pr_info("gpio_request failed\n");
    
    ret = gpio_direction_output(ledgpionum, 1);
    if (ret)
        pr_info("gpio_request failed\n");

    return 0;
}
c 复制代码
//vi arch/arm/boot/dts/imx6ull-alientek-emmc.dts


	puteleds {
		#address-cells = <1>;
		#size-cells = <1>;
		
		compatible = "pute-driver-leds";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_puteled>;
		led-gpio = <&gpio1 3 0>;
		status = "okay";
	};
2.完整代码
复制代码
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <linux/of_gpio.h>

static ssize_t led_read(struct file *fp, char __user *puser, size_t n, loff_t *off);
static ssize_t led_write(struct file *fp, const char __user *puser, size_t n, loff_t *off);

static int ledstat = 0;
static struct device_node *pdtsnode;
static int ledgpionum;
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = led_read,
    .write = led_write,
};

static struct miscdevice misc_device = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "misc_led",
    .fops = &fops,
};

static ssize_t led_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{
    unsigned long ret = 0;
    ret = copy_to_user(puser, &ledstat, sizeof(ledstat));
    if (ret)
        pr_info("copy_to_user failed");

    return 0;
}            

static ssize_t led_write(struct file *fp, const char __user *puser, size_t n, loff_t *off)
{
	unsigned long value = 0;
	unsigned long ret = 0;

    ret = copy_from_user(&value, puser, sizeof(value));
    if (ret)
        pr_info("copy_from_user failed");

    if (1 == value)
    {
        gpio_set_value(ledgpionum, 0);
        ledstat = 1;
    }
    else if (0 == value)
    {
        gpio_set_value(ledgpionum, 1);
        ledstat = 0;
    }

    return 0;
}

static int __init led_init(void)
{
    int ret = 0;
    
    ret = misc_register(&misc_device);
    if (ret)
        pr_info("misc register led failed\n");

    pdtsnode = of_find_node_by_path("/puteleds");
    if (!pdtsnode)
        pr_info("of_find_node_by_path failed\n");

    ledgpionum = of_get_named_gpio(pdtsnode, "led-gpio", 0);
    if (ledgpionum < 0)
        pr_info("of_get_named_gpio failed\n");

    ret = gpio_request(ledgpionum, "puteled");
    if (ret)
        pr_info("gpio_request failed\n");
    
    ret = gpio_direction_output(ledgpionum, 1);
    if (ret)
        pr_info("gpio_request failed\n");

    return 0;
}

static void __exit led_exit(void)
{
    int ret = 0;
    
    gpio_free(ledgpionum);
    ret = misc_deregister(&misc_device);
    if (ret)
        pr_info("misc deregister led failed\n");

    return;
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("haiersen");

十四:iic子系统---2440

1. iIC总线驱动

IIC总线驱动: (主要实现与设备无关的IIC读写时序并提供操作读写的方法)

2440 只有一个iic接口 dev/iic/0 0接口

2. adapter

i2c_adapter:总线控制器,读写都在这里控制算法algorithm,有读写函数

  • 代表一条 I²C 总线控制器(比如 SoC 上的 I²C0、I²C1 控制器)。
  • 每个 adapter 都对应一条总线(编号从 0 开始)。
  • 里面包含了访问硬件的函数指针(master_xfersmbus_xfer),用来实现具体的读写。
  • 对应物理层面上"谁来发时钟、发起 start/stop 信号"。

关键结构体(简化版):

c 复制代码
struct i2c_adapter {
    struct module *owner;
    struct i2c_algorithm *algo;  // 算法实现,比如如何发起I²C传输
    struct device dev;           // 代表一个总线设备
    int nr;                      // 总线编号,比如 i2c-0, i2c-1
};

3.应用层直接调用iic驱动总线

c 复制代码
//linux@ubuntu:~/nfs/rootfs$ vi lm75.c
//linux@ubuntu:~/nfs/rootfs$ arm-linux-gcc lm75.c -o lm75


#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/stat.h>
#include <sys/ioctl.h>

int main(int argc, const char *argv[])
{
	unsigned char lm75_addr = 0x48;
	unsigned char reg_addr = 0;
	unsigned char data[2] = {0};

	int fd = open("/dev/i2c/0",O_RDWR);
	if(fd < 0)
	{
		perror("open /dev/i2c/0 failed\n");
		return -1;
	}

	ioctl(fd,I2C_SLAVE,lm75_addr);

	while(1)
	{
		write(fd,&reg_addr,sizeof reg_addr);
		read(fd,data,sizeof data);
		float temp = 0.5 * (((data[0] << 8) | data[1]) >> 7);
			//0在高位MSB,1在低位LSB    >>7 把没用的位置去掉
        printf("temp = %.1f\n",temp);
		sleep(2);
	}

	close(fd);

	return 0;
}

4.通过 core 核心层调用iic总线驱动

1. client
c 复制代码
struct i2c_adapter* i2c_get_adapter(int id)  //只要id就行
{
	struct i2c_adapter *adapter;

	mutex_lock(&core_lock);
	adapter = idr_find(&i2c_adapter_idr, id);
	if (adapter && !try_module_get(adapter->owner))
		adapter = NULL;

	mutex_unlock(&core_lock);
	return adapter;
}
c 复制代码
#define I2C_BOARD_INFO(dev_type, dev_addr) 
//.type = dev_type, .addr = (dev_addr)

struct i2c_board_info {
	char		type[I2C_NAME_SIZE];	//name
	unsigned short	flags;			
	unsigned short	addr;				//地址
	void		*platform_data;
	struct dev_archdata	*archdata;
	int		irq;
};
//其他按需
c 复制代码
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/i2c.h>

#define DEV_NAME "lm75"
#define LM75_ADDR 0x48
#define I2C_ADAPTER_NUM 0

static struct i2c_board_info info = 
{
	I2C_BOARD_INFO(DEV_NAME, LM75_ADDR)	//就要地址和name
};

struct i2c_client * pclient;

static int __init lm75_client_init(void)
{
	static struct i2c_adapter * padapter = NULL;	//创建adapter,然后之后匹配对应的一组
	padapter =  i2c_get_adapter(I2C_ADAPTER_NUM);	//只要对应id
	if(NULL == padapter)
		goto err_get_adatper;

	pclient = i2c_new_device(padapter, &info);	//pclient 拿到对应从机的地址,id,name
	if(NULL == pclient)
		goto err_new_device;

	printk("lm75_client_init  ...\n");
	return 0;

err_new_device:
err_get_adatper:
	printk("lm75_client_init  failed...\n");
	return -1;
}

static void __exit lm75_client_exit(void)
{
	i2c_unregister_device(pclient);		//注销
}

module_init(lm75_client_init);
module_exit(lm75_client_exit);
MODULE_LICENSE("GPL");
2. driver
c 复制代码
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/i2c.h>

#define DEV_NAME "lm75"
static struct i2c_client * lm75_client;	//保存lm75_client,因为

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

static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{
	//  write reg_addr to lm75		//0x48
	//  read  2 bytes data from lm75		//0x48
	
	unsigned char lm75_reg_addr = 0;
	unsigned char data[2] = {0};
	struct i2c_msg msg;
	msg.addr = 0x48;
	msg.flags = 0;  //iic write
	msg.buf = &lm75_reg_addr;
	msg.len = sizeof(lm75_reg_addr);
	lm75_client->adapter->algo->master_xfer(lm75_client->adapter, &msg, 1);	

	msg.addr = 0x48;
	msg.flags = I2C_M_RD;  //iic read
	msg.buf = data;
	msg.len = sizeof(data);
	lm75_client->adapter->algo->master_xfer(lm75_client->adapter, &msg, 1);	

	copy_to_user(buf, data, sizeof(data));
	
	return sizeof(data);
}

static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
	return 0;
}

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

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.release = close
};

static struct miscdevice misc = 
{
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEV_NAME,
	.fops = &fops
};

static int probe(struct i2c_client * pclient, const struct i2c_device_id * dev_id)	//client拿到probe
{
	int ret = misc_register(&misc);
	if(ret < 0)
		goto err_misc_register;
	lm75_client = pclient;	//匹配

	printk("lm75  probe ...\n");
	return 0;

err_misc_register:
	misc_deregister(&misc);
	printk("lm75_register  failed\n");
	return ret;	

	return 0;
}

static int remove(struct i2c_client * pclient)
{
	misc_deregister(&misc);
	printk("lm75 remove   ....\n");

	return 0;
}

----------------------------------------------------------------

//拿id
static struct i2c_device_id lm75_id_table[] = 
{
	[0] = {.name = DEV_NAME}
};


//类似于platform
static struct i2c_driver lm75_driver = 
{
	.probe = probe,		//匹配对应
	.remove = remove,
	.driver = 
	{
		.name = "lm75_driver"	//匹配用的表示字符串,在设备中匹配,设备树匹配?
	},
	.id_table = lm75_id_table	//拿id
};



static int __init lm75_driver_init(void)
{
	int ret = i2c_add_driver(&lm75_driver); //注册对应名字
	if(ret < 0)
		return ret;	//搞一下报错处理  goto xxx

	printk("lm75_driver_init  ...\n");
	return 0;
}

static void __exit lm75_driver_exit(void)
{
	i2c_del_driver(&lm75_driver);	//注销
	printk("lm75_driver_exit  ...\n");
}

module_init(lm75_driver_init);
module_exit(lm75_driver_exit);
MODULE_LICENSE("GPL");
3. 关于 i2c_driver
c 复制代码
// 定义支持的 I2C 设备 ID 列表,
// 当内核扫描到 i2c_client->name 与这里的 name 匹配时,
// 就会把匹配到的 id_table 元素传给 probe()
static struct i2c_device_id lm75_id_table[] = 
{
    [0] = {.name = DEV_NAME}
};

// 定义 I2C 驱动
static struct i2c_driver lm75_driver = 
{
    .probe  = probe,     // 设备和驱动匹配成功后调用
    .remove = remove,    // 设备移除或驱动卸载时调用
    .driver = 
    {
        .name = "lm75_driver"   // 驱动名字(匹配用):
                                // ① 和 i2c_device_id[].name 匹配
                                // ② 或和设备树(dts)中 compatible 匹配
    },
    .id_table = lm75_id_table   // 指向支持的设备 ID 表,内核用来做匹配
};
4.关于adapter实现读写的流程
c 复制代码
lm75_client->adapter->algo->master_xfer(lm75_client->adapter, &msg, 1);

//lm75_client的adapter的算法algorithm的master_xfer函数,这个函数才是实现iic驱动的读写操作

//感恩😰
4. msg
c 复制代码
struct i2c_msg {
	__u16 addr;	/* slave address			*/
	__u16 flags;
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RD		0x0001	/* read data, from slave to master */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
	__u16 len;		/* msg length				*/
	__u8 *buf;		/* pointer to msg data			*/
};


//关于I2C_M_RD本质上是if else     所以write是0
if(I2C_M_RD) 
{
    //read
}else
{
    //write
}
5. 全部*
C 复制代码
//ls
/*
lm754_client.c      lm754_client.o      lm754_driver.mod.o
lm754_client.ko     lm754_driver.c      lm754_driver.o
lm754_client.mod.c  lm754_driver.ko     
lm754_client.mod.o  lm754_driver.mod.c  
*/
linux@ubuntu:~/ARM/linux-2.6.32.2$ cp drivers/char/lm754_*.ko ~/nfs/rootfs

5. 用户程序

c 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>

int main(int argc, const char *argv[])
{
#if 0		//应用层直接操作iic总线驱动
	unsigned char lm75_addr = 0x48; 
	unsigned char reg_addr = 0;
	unsigned char data[2] = {0};

	int fd = open("/dev/i2c/0", O_RDWR);
	if(fd < 0)
	{
		perror("open i2c/0 failed\n");
		return -1;
	}
	
	ioctl(fd, I2C_SLAVE, lm75_addr);

	while(1)
	{
		write(fd, &reg_addr, sizeof(reg_addr));
		read(fd, data, sizeof(data));
		float temp =  0.5 * (((data[0] << 8) | data[1]) >> 7);
		printf("temp = %.1f\n", temp);
		sleep(2);
	}

	close(fd);
#endif
	
    ////应用层通过adapter,核心层操作iic总线驱动
	int fd = open("/dev/lm75", O_RDWR);
	if(fd < 0)
	{
		perror("open lm75")	;
		return -1;
	}

	while(1)
	{
		unsigned char data[2] = {0};
		read(fd, data, sizeof(data));

		float temp =  0.5 * (((data[0] << 8) | data[1]) >> 7);
		printf("temp = %.1f\n", temp);
		sleep(2);
	}

	close(fd);
	return 0;
}

6.流程

复制代码
[root@FriendlyARM /]# insmod lm754_client.ko 
lm75_client_init  ...
[root@FriendlyARM /]# insmod lm754_driver.ko 
lm75  probe ...
lm75_driver_init  ...

7.如果用设备树的话

clien就不用了,本质上就是device --- 都是设备信息

相关推荐
李小白2020020210 小时前
windows 10系统安装arm虚拟机
arm开发
亿道电子Emdoor2 天前
【ARM】PACK包管理
arm开发
wypywyp3 天前
基于arm芯片的驱动开发——温湿度传感器dht11
arm开发·驱动开发
亿道电子Emdoor3 天前
【ARM】MDK如何实现使用Hex文件完成程序烧录
arm开发·stm32·单片机
彻骨寒风4 天前
在麒麟 ARM (aarch64)安装OpenJDK11和elasticsearchkibana
运维·arm开发·jenkins
2301_1472583694 天前
ARM - GPIO 标准库开发
arm开发
Yeats_Liao4 天前
X86、X64 与 ARM:架构的剖析与比较
arm开发·架构
ShiMetaPi5 天前
【GM3568JHF】FPGA+ARM异构开发板 使用指南:显示与触摸
arm开发·嵌入式硬件·fpga开发·rk3568
亿道电子Emdoor5 天前
【ARM】MDK出现:Unable to find ARM libraries
arm开发·stm32·单片机