自旋锁/互斥锁 设备树 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 --- 都是设备信息

相关推荐
AI+程序员在路上1 小时前
Nand Flash与EMMC区别及ARM开发板中的应用对比
arm开发
17(无规则自律)7 小时前
深入浅出 Linux 内核模块,写一个内核版的 Hello World
linux·arm开发·嵌入式硬件
梁洪飞19 小时前
内核的schedule和SMP多核处理器启动协议
linux·arm开发·嵌入式硬件·arm
代码游侠1 天前
学习笔记——Linux字符设备驱动
linux·运维·arm开发·嵌入式硬件·学习·架构
syseptember2 天前
Linux网络基础
linux·网络·arm开发
代码游侠2 天前
学习笔记——Linux字符设备驱动开发
linux·arm开发·驱动开发·单片机·嵌入式硬件·学习·算法
程序猿阿伟2 天前
《Apple Silicon与Windows on ARM:引擎原生构建与模拟层底层运作深度解析》
arm开发·windows
wkm9562 天前
在arm64 ubuntu系统安装Qt后编译时找不到Qt3DExtras头文件
开发语言·arm开发·qt
unicrom_深圳市由你创科技2 天前
基于ARM+DSP+FPGA异构计算架构的高速ADC采集卡定制方案
arm开发·fpga开发
松涛和鸣2 天前
DAY69 Practical Guide to Linux Character Device Drivers
linux·服务器·arm开发·数据库·单片机·嵌入式硬件