gpio 字符设备驱动
#define GPIO_OFT(x) ((x) - 0x56000000)
#define GPFCON (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000050)))
#define GPFDAT (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000054)))
/* 应用程序对设备文件/dev/leds执行open(...)时,
* 就会调用s3c24xx_leds_open函数
*/
static int s3c24xx_leds_open(struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev);
switch(minor)
{
case 0: /* /dev/leds */
{
// 配置3引脚为输出
//s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
GPFCON &= ~(0x3<<(4*2));
GPFCON |= (1<<(4*2));
//s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
GPFCON &= ~(0x3<<(5*2));
GPFCON |= (1<<(5*2));
//s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);
GPFCON &= ~(0x3<<(6*2));
GPFCON |= (1<<(6*2));
// 都输出0
//s3c2410_gpio_setpin(S3C2410_GPF4, 0);
GPFDAT &= ~(1<<4);
//s3c2410_gpio_setpin(S3C2410_GPF5, 0);
GPFDAT &= ~(1<<5);
//s3c2410_gpio_setpin(S3C2410_GPF6, 0);
GPFDAT &= ~(1<<6);
down(&leds_lock);
leds_status = 0x0;
up(&leds_lock);
break;
}
case 1: /* /dev/led1 */
{
s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
s3c2410_gpio_setpin(S3C2410_GPF4, 0);
down(&leds_lock);
leds_status &= ~(1<<0);
up(&leds_lock);
break;
}
case 2: /* /dev/led2 */
{
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
s3c2410_gpio_setpin(S3C2410_GPF5, 0);
leds_status &= ~(1<<1);
break;
}
case 3: /* /dev/led3 */
{
s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);
s3c2410_gpio_setpin(S3C2410_GPF6, 0);
down(&leds_lock);
leds_status &= ~(1<<2);
up(&leds_lock);
break;
}
}
return 0;
}
/* 这个结构是字符设备驱动程序的核心
* 当应用程序操作设备文件时所调用的open、read、write等函数,
* 最终会调用这个结构中指定的对应函数
*/
static struct file_operations s3c24xx_leds_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = s3c24xx_leds_open,
.read = s3c24xx_leds_read,
.write = s3c24xx_leds_write,
};
/*
* 执行insmod命令时就会调用这个函数
*/
static int __init s3c24xx_leds_init(void)
//static int __init init_module(void)
{
int ret;
int minor = 0;
gpio_va = ioremap(0x56000000, 0x100000);
if (!gpio_va) {
return -EIO;
}
/* 注册字符设备
* 参数为主设备号、设备名字、file_operations结构;
* 这样,主设备号就和具体的file_operations结构联系起来了,
* 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数
* LED_MAJOR可以设为0,表示由内核自动分配主设备号
*/
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
if (ret < 0) {
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
leds_class = class_create(THIS_MODULE, "leds");
if (IS_ERR(leds_class))
return PTR_ERR(leds_class);
leds_class_devs[0] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, 0), NULL, "leds"); /* /dev/leds */
for (minor = 1; minor < 4; minor++) /* /dev/led1,2,3 */
{
leds_class_devs[minor] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, minor), NULL, "led%d", minor);
if (unlikely(IS_ERR(leds_class_devs[minor])))
return PTR_ERR(leds_class_devs[minor]);
}
printk(DEVICE_NAME " initialized\n");
return 0;
}
/*
* 执行rmmod命令时就会调用这个函数
*/
static void __exit s3c24xx_leds_exit(void)
{
int minor;
/* 卸载驱动程序 */
unregister_chrdev(LED_MAJOR, DEVICE_NAME);
for (minor = 0; minor < 4; minor++)
{
class_device_unregister(leds_class_devs[minor]);
}
class_destroy(leds_class);
iounmap(gpio_va);
}
/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
应用程序和vfs之间的接口是系统调用,而vfs与文件系统以及设备文件之间的接口是file_operations成员
函数。
由于字符设备没有类似磁盘的,ext,fat等文件系统(和vfs进行对接),
所以就直接由设备驱动提供了。file_operatitions是字符设备驱动的核心。
gpio 平台设备驱动
linux 驱动模型的作用:
- 设备驱动模型实现uevent机制,调用应用层的medv来自动创建设备文件
- 设备驱动模型通过sysfs文件系统向用户层提供设备驱动视图
- 设备驱动模型提供统一的电源管理机制
- 设备驱动模型提供各种对象实例的引用计数,防止对象被应用层误删。设备模型的所有数据结构均是继承kobject而来,而kobject就提供基础的计数功能
- 设备驱动模型提供多一种方式给应用层,用户和内核可以通过sysfs进行交互,如通过修改/sys目录下设备的文件内容,即可以直接修改设备对应的参数。
static int led_open(struct inode *inode, struct file *file)
{
//printk("first_drv_open\n");
/* 配置为输出 */
*gpio_con &= ~(0x3<<(pin*2));
*gpio_con |= (0x1<<(pin*2));
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = led_open,
.write = led_write,
};
static int led_probe(struct platform_device *pdev)
{
struct resource *res;
/* 根据platform_device的资源进行ioremap */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpio_con = ioremap(res->start, res->end - res->start + 1);
gpio_dat = gpio_con + 1;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
pin = res->start;
/* 注册字符设备驱动程序 */
printk("led_probe, found led\n");
major = register_chrdev(0, "myled", &led_fops);
cls = class_create(THIS_MODULE, "myled");
class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
return 0;
}
static int led_remove(struct platform_device *pdev)
{
/* 卸载字符设备驱动程序 */
/* iounmap */
printk("led_remove, remove led\n");
class_device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "myled");
iounmap(gpio_con);
return 0;
}
struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
}
};
static int led_drv_init(void)
{
platform_driver_register(&led_drv);
return 0;
}
static void led_drv_exit(void)
{
platform_driver_unregister(&led_drv);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
可以看出,如果使用平台设备驱动的方式去实现的话,就不必将,设备寄存器的信息 再驱动代码里面写死,而是通过设备树,或者板级信息,拿到设备寄存器相关的信息。实现设备和驱动的分离。不过这个例子并没有使用gpio子系统,只是再字符设备驱动之上套了一层壳而已。所有仍需要手动调用,class_device_create,去创建/dev ,/sys路径下的设备文件。以便用户可以访问该驱动。
gpio 子系统驱动代码
51 struct pl061_gpio {
52 spinlock_t lock;
53
54 void __iomem *base;
55 struct gpio_chip gc;//关键结构体,还是gpio_chip
56
57 #ifdef CONFIG_PM
58 struct pl061_context_save_regs csave_regs;
59 #endif
60 }
236 static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
237 {
238 struct device *dev = &adev->dev;
239 struct pl061_platform_data *pdata = dev_get_platdata(dev);
240 struct pl061_gpio *chip;
241 int ret, irq, i, irq_base;
242
243 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
244 if (chip == NULL)
245 return -ENOMEM;
246
247 if (pdata) {
248 chip->gc.base = pdata->gpio_base;
249 irq_base = pdata->irq_base;
250 if (irq_base <= 0) {
251 dev_err(&adev->dev, "invalid IRQ base in pdata\n");
252 return -ENODEV;
253 }
254 } else {
236,1 57%
253 }
254 } else {
255 if (dev->of_node) {
256 i = of_alias_get_id(dev->of_node, "gpio");
257 chip->gc.base = i * PL061_GPIO_NR;
258 }
259
260 if (chip->gc.base < 0)
261 chip->gc.base = -1;
262 irq_base = 0;
263 }
264
265 chip->base = devm_ioremap_resource(dev, &adev->res);
266 if (IS_ERR(chip->base))
267 return PTR_ERR(chip->base);
268
269 spin_lock_init(&chip->lock);
270
271 if (of_property_read_bool(dev->of_node, "gpio-ranges")){
272 chip->gc.request = pl061_gpio_request;
273 chip->gc.free = pl061_gpio_free;
274 }
275 chip->gc.direction_input = pl061_direction_input;
276 chip->gc.direction_output = pl061_direction_output;
277 chip->gc.get = pl061_get_value;
278 chip->gc.set = pl061_set_value;
279 chip->gc.ngpio = PL061_GPIO_NR;
280 chip->gc.label = dev_name(dev);
281 chip->gc.dev = dev;
282 chip->gc.owner = THIS_MODULE;
283
284 ret = gpiochip_add(&chip->gc);//将填充好的结构体,注册进gpio子系统
285 if (ret)
286 return ret;
...
}
398 static struct amba_driver pl061_gpio_driver = {
399 .drv = {
400 .name = "pl061_gpio",
401 #ifdef CONFIG_PM
402 .pm = &pl061_dev_pm_ops,
403 #endif
404 },
405 .id_table = pl061_ids,
406 .probe = pl061_probe,
407 };
408
409 static int __init pl061_gpio_init(void)
410 {
411 return amba_driver_register(&pl061_gpio_driver);
412 }
413 module_init(pl061_gpio_init);
使用Linux自带的gpio子系统驱动框架的,就不用再手动,创建/dev目录下的设备文件。因为驱动框架会为用户自动做好。
gpio子系统介绍文档:
https://xuesong.blog.csdn.net/article/details/108909011