十二:锁/互斥/信号量/自旋
锁持有时间
长-------互斥锁
短-------自旋锁
自旋锁不会使线程状态发生切换
一直处于用户态,即线程—直都是运行的;
不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。
1.自旋锁
#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.互斥锁
#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.信号量
#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.设备树
//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>;
};
make imx6ull-alientek-emmc.dtb //make 制定的设备树,加后缀就行
cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb ~/tftpboot/ //更新tftpboot
//查找设备树节点
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.完整代码
#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搞定
make -f Makefile1
//vi Makefile1
modulename=led
all:
make -C $(modulename)_app_96
make -C $(modulename)_drv_96
//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
// 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.关于设备树文件
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;
}
//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_xfer
、smbus_xfer
),用来实现具体的读写。 - 对应物理层面上“谁来发时钟、发起 start/stop 信号”。
关键结构体(简化版):
struct i2c_adapter {
struct module *owner;
struct i2c_algorithm *algo; // 算法实现,比如如何发起I²C传输
struct device dev; // 代表一个总线设备
int nr; // 总线编号,比如 i2c-0, i2c-1
};
3.应用层直接调用iic
驱动总线
//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,®_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
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;
}
#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;
};
//其他按需
#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
#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
// 定义支持的 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实现读写的流程
lm75_client->adapter->algo->master_xfer(lm75_client->adapter, &msg, 1);
//lm75_client的adapter的算法algorithm的master_xfer函数,这个函数才是实现iic驱动的读写操作
//感恩😰
4. msg
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. 全部*
//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. 用户程序
#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, ®_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
— 都是设备信息