驱动第八天

驱动第八天
操作IO内存步骤:
1. 申请IO内存
2. 物理地址和虚拟地址映射
3. 操作IO内存
4. 解映射
5. 释放IO内存

关于private_date私有数据:
我想在ioctl中获取led的结构地址,可以用下面的路径找到:
1. led   -> cdev
2. cdev  -> inode
3. inode -> open
4. led   -> private_date
这样做的目地是为了不使用全局变量,使驱动成为可重入的。

container_of的原理基础:
 驱动第八天

【学习总结】

/*********************RTC驱动***************************/

/*

 * RTC借助电池供电,它通常还有产生周期性中断以及产生闹钟中断的能力,是一种典型的字符设备。/drivers/rtc/rtc-dev.c中实现了通用的字符设备驱动层,它实现

 * 了file_operations的成员函数以及一些通用的控制代码,并向底层导出了注册和注销函数rtc_class_ops结构体用于描述底层的RTC硬件操作drivers/rtc/rtc-s3c.c

 * 实现了RTC驱动,其注册RTC以及绑定了rtc_class_ops相关数据结构和接口函数在rtc.hrtc.c

 */

#include <linux/rtc.h>

#include <plat/regs-rtc.h>

struct rtc_device

{

  struct device dev;

  struct module *owner;

 

  int id;

  char name[RTC_DEVICE_NAME_SIZE];

 

  const struct rtc_class_ops *ops;

  struct mutex ops_lock;

 

  struct cdev char_dev;

  unsigned long flags;

 

  unsigned long irq_data;

  spinlock_t irq_lock;

  wait_queue_head_t irq_queue;

  struct fasync_struct *async_queue;

 

  struct rtc_task *irq_task;

  spinlock_t irq_task_lock;

  int irq_freq;

  int max_user_freq;

#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL

  struct work_struct uie_task;

  struct timer_list uie_timer;

  /* Those fields are protected by rtc->irq_lock */

  unsigned int oldsecs;

  unsigned int uie_irq_active:1;

  unsigned int stop_uie_polling:1;

  unsigned int uie_task_active:1;

  unsigned int uie_timer_active:1;

#endif

};

 

struct rtc_class_ops {   //用于描述底层的RTC硬件操作

  int (*open)(struct device *);

  void (*release)(struct device *);

  int (*ioctl)(struct device *, unsigned int, unsigned long);

  int (*read_time)(struct device *, struct rtc_time *);

  int (*set_time)(struct device *, struct rtc_time *);

  int (*read_alarm)(struct device *, struct rtc_wkalrm *);

  int (*set_alarm)(struct device *, struct rtc_wkalrm *);

  int (*proc)(struct device *, struct seq_file *);

  int (*set_mmss)(struct device *, unsigned long secs);

  int (*irq_set_state)(struct device *, int enabled);

  int (*irq_set_freq)(struct device *, int freq);

  int (*read_callback)(struct device *, int data);

  int (*alarm_irq_enable)(struct device *, unsigned int enabled);

  int (*update_irq_enable)(struct device *, unsigned int enabled);

};

 

struct rtc_device *rtc_device_register(const char *name,struct device *dev,

       const struct rtc_class_ops *ops,struct module *owner); //注册RTC

extern void rtc_device_unregister(struct rtc_device *rtc);  //注销RTC

 

一般设计步骤:

1struct rtc_device *rtc;

2:硬件相关的操作

3static const struct rtc_class_ops s3c_rtcops = {

  .open   = s3c_rtc_open,

  .release  = s3c_rtc_release,

  .read_time  = s3c_rtc_gettime,

  .set_time = s3c_rtc_settime,

  .read_alarm = s3c_rtc_getalarm,

  .set_alarm  = s3c_rtc_setalarm,

  .irq_set_freq = s3c_rtc_setfreq,

  .irq_set_state  = s3c_rtc_setpie,

  .proc         = s3c_rtc_proc,

};

4:注册

5:上述函数的实现

/**************************end***********************/


【学习代码】
 1. 使用platform总线操作led灯
【头文件led.h】

#ifndef __LED_H__

#define __LED_H__

 

#define LED_ON  _IO(‘k‘, 0)

#define LED_OFF _IO(‘k‘, 1)

 

#endif // __LED_H__


【主驱动led_driver.c】

/*

 * io内存资源放到BSP linux-2.6.35-farsight\arch\arm\mach-s5pc100\mach-smdkc100

 */

#include <linux/init.h>

#include <linux/module.h>

#include <linux/platform_device.h>

#include <linux/cdev.h>

#include <linux/fs.h>

#include <linux/slab.h>

 

// ioremap

#include <asm/io.h>

 

#include "led.h"

 

struct led_t {

  // 设备类型(字符设备、块设备、网络设备)

  struct cdev led_cdev;

  // 设备号

  dev_t devno;

  // 设备类

  struct class *class;

  // 设备

  struct device *dev;

  // 设备本身特征

  // 1. 描述led设备本身

  int on;                                       // 0 - turn off 1-turn on    

  // lock

  // 操作设备的寄存器的虚拟地址

  unsigned long* GPG3CON;

  unsigned long* GPG3DAT;

};

 

int led_open(struct inode *inode, struct file *filp)

{

  filp->private_data = (void *)container_of(inode->i_cdev, struct led_t, led_cdev);

  printk("char open\n");

  return 0;

}

 

long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

{

  struct led_t *led = (struct led_t *)filp->private_data;

  long ret = 0;

  unsigned long reg;

 

  printk("led_ioctl: %p\n", led);

  switch (cmd) {

    // 5. 实现led开关

    case LED_ON:

        reg = readl(led->GPG3DAT);

        reg |= 1;

        writel(reg, led->GPG3DAT);

      break;

     

    case LED_OFF:

        reg = readl(led->GPG3DAT);

        reg &= ~1;

        writel(reg, led->GPG3DAT);

      break;

       

    default:

      ret = -ENOTTY;

      break;

  }

 

  return 0;

}

 

int led_release(struct inode *inode, struct file *filp)

{

  printk("char release\n");

  return 0;

}

 

struct file_operations fops = {

  .owner = THIS_MODULE,            

  .open  = led_open,

  .unlocked_ioctl = led_ioctl,

  .release = led_release,

};

 

int led_probe(struct platform_device *dev)

{

  int ret = 0;

  struct led_t *led;

  struct resource * res;

  unsigned long reg;

 

  printk("platform: match ok!\n");

 

  led = (struct led_t*)kmalloc(sizeof(struct led_t), GFP_KERNEL);

  if (NULL == led) {

    ret = -ENOMEM;                         

    goto exit;

  }

  memset(led, 0, sizeof(struct led_t));

  platform_set_drvdata(dev, led);

 

  // 1. 获取资源

  /*

   * @brief         bsp获取平台设备资源

   * @param[in]     dev                         平台设备

   * @param[in]     type                        资源类型(flags)

   * @param[in]     num                         资源编号

   * @return        资源

   */

  // struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num)

  res = platform_get_resource(dev, IORESOURCE_MEM, 0);

  if (NULL == res) {

    ret = -ENOMEM;

    goto platfrom_get_resource_err;

  }

  printk("res: %08x - %08x, size= %d\n", res->start, res->end, res->end - res->start + 1);

 

  /* 如果是在bsp中设置io内存,不需要再申请

  if (NULL == request_mem_region(res->start, res->start - res->end + 1, "led")) {

    ret = -ENOMEM;

    goto request_mem_region_err;

  }*/

     

  led->GPG3CON = (unsigned long *)ioremap(res->start, res->end - res->start + 1);

  if (NULL == led->GPG3CON) {

    printk("ioremap\n");

    ret = -EBUSY;

    goto ioremap_err;

  }

  led->GPG3DAT = led->GPG3CON + 1;

 

  reg = readl(led->GPG3CON);

  reg &= ~0xf;

  reg |= 1;

  writel(reg, led->GPG3CON);

 

  reg = readl(led->GPG3DAT);

  reg &= ~1;

  writel(reg, led->GPG3DAT);

 

  cdev_init(&led->led_cdev, &fops);

  led->led_cdev.owner = THIS_MODULE;

 

  ret = alloc_chrdev_region(&led->devno, 0, 1, "led device");

  if (ret) {

    printk("alloc_chrdev_region\n");

    goto alloc_chrdev_region_err;

  }

 

  ret = cdev_add(&led->led_cdev, led->devno, 1);

  if (ret) {

    printk("cdev_add\n");

    goto cdev_add_err;

  }

 

  led->class = class_create(THIS_MODULE, "led");

  if (IS_ERR(led->class)) {

    ret = PTR_ERR(led->class);

    goto class_create_err;

  }

 

  led->dev = device_create(led->class, NULL, led->devno, NULL, "led");

  if (IS_ERR(led->dev)) {

    ret = PTR_ERR(led->dev);

    goto device_create_err;

  }

 

  goto exit;

 

device_create_err:

  class_destroy(led->class);

class_create_err:

  cdev_del(&led->led_cdev);

cdev_add_err:

  unregister_chrdev_region(led->devno, 1); 

alloc_chrdev_region_err:

  iounmap(led->GPG3CON);

ioremap_err:

 

// 如果是在bsp中设置io内存,不需要再申请

//  release_mem_region(0xE03001C0, 8);

//request_mem_region_err:

platfrom_get_resource_err:

      kfree(led);

exit:

      return ret;

}

 

int led_remove(struct platform_device *dev)

{

  struct led_t *led = (struct led_t *)platform_get_drvdata(dev);

   

  printk("platform: driver remove\n");

 

  device_destroy(led->class, led->devno);

  class_destroy(led->class);

  cdev_del(&led->led_cdev);

  unregister_chrdev_region(led->devno, 1);

  iounmap(led->GPG3CON);

//  release_mem_region(0xE03001C0, 8);

  kfree(led);

 

  return 0;

}

 

struct platform_driver led_driver = {

  .probe = led_probe,

  .remove = __devexit_p(led_remove),

  .driver = {

    .name = "led_device",

  },

};

 

int __init led_init(void)

{

  platform_driver_register(&led_driver);

  return 0;

}

 

void __exit led_exit(void)

{

     platform_driver_unregister(&led_driver);

}

 

module_init(led_init);

module_exit(led_exit); 

MODULE_LICENSE("GPL");

 

【测试程序led_app.c】

#include <stdio.h>

#include <sys/types.h>

#include <sys/ioctl.h>

#include <unistd.h>

#include <fcntl.h>

#include <stdlib.h>

#include "led.h"

 

int main (void)

{

  int fd;

 

  fd = open ("/dev/led", O_RDWR);

  if (fd < 0) {

    printf ("fd open failed\n");

    exit(0);

  }

 

  printf ("\n/dev/chardev opened, fd=%d\n",fd);

  ioctl(fd, LED_ON);

  sleep(10);

  ioctl(fd, LED_OFF);

  close (fd);

  printf ("/dev/hello closed :)\n");

 

  return 0;

}


BSP linux-2.6.35-farsight\arch\arm\mach-s5pc100\mach-smdkc100中添加:

// 1. 新建平台总线设备

struct resource led_resources[] = {

  [0] = {

    .start  = 0xE03001C0,        // io内存开始

    .end  = 0xE03001C0 + 7,       // io内存结束(包含结束地址本身)

    .flags  = IORESOURCE_MEM,

  }

};

 

static struct platform_device led_device = {

  .name = "led_device",         // 跟驱动里面的name相同

  .id = -1,                       // 用于多个相近设备的设备id

 

  // 添加IO内存资源

  .num_resources = ARRAY_SIZE(led_resources),    

  .resource = led_resources,

};

 

 2. 加自旋锁之后的led灯
在前面的程序上只需要修改部分代码就行了:
a. 在struct led_t结构体中添加自旋锁的定义:
   spinlock_t lock;
b. 在led_ioctl函数中修改操作为:
   驱动第八天
   其他的和前面的一样。

 3. 中断操作实例
 /*

 * 中断例子(cat /proc/interrupts 命令查看中断是否申请成,并且中断次数)

 */

#include <linux/init.h>

#include <linux/module.h>

 

// 1. 头文件

#include <linux/interrupt.h>

#include <linux/irq.h>

 

// 设备结构体

struct key_t {

 

} key;

 

// 2.实现中断isr

/*

 * @brief           中断处理函数

 * @param[in]       irq                         中断号

 * @param[in]       dev                         设备结构体指针

 * @return          处理结果

 *                  @li IRQ_NONE                没有处理中断(中断共享)

 *                  @li IRQ_HANDLED             已经发生中断

 *                  @li IRQ_WAKE_THREAD         唤醒irq处理线程

 * @notes           本函数是内核调用

 */

irqreturn_t key_hander(int irq, void *dev)

{

  printk("key_hander\n");

  // 1. 不能休眠

  // 2. 时间不能太长

  // 3. 如果需要,调度中断下半部

  return IRQ_HANDLED;

}

 

int __init int_demo_init(void)

{

  int ret = 0;

  // 3. 申请中断

  /*

   * @brief           申请中断

   * @param[in]       irq               中断号

   * @param[in]       handler           中断处理函数

   * @param[in]       flags             中断处理标志(中断促发方式,中断是否共享,中断是否可以嵌套)

   * @param[in]       name              中断名字(和设备名字相同)

   * @param[in]       dev               设备结构体

   * @return          申请结果

   *                  @li 0             申请成功

   *                  @li < 0           错误码

   */

  // int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);

  // IRQ_EINT(1)      外部中断1,不一定是中断1

  // IRQF_DISABLED    执行中断处理函数期间,禁止中断

  ret = request_irq(IRQ_EINT(1), key_hander, IRQF_TRIGGER_FALLING | IRQF_DISABLED, "key", &key);

  return 0;

}

 

void __exit int_demo_exit(void)

{

  // 3.

  /*

   * @brief           释放中断

   * @param[in]       irq                         中断号

   * @param[in]       dev                         设备结构体

   */

  // void free_irq(unsigned int irq, void *dev);

  free_irq(IRQ_EINT(1), &key);

}

 

module_init(int_demo_init);

module_exit(int_demo_exit);

 

MODULE_LICENSE("GPL"); 

驱动第八天

上一篇:使用ViewPager实现图片左右切换(有实心和空心和TextView随图片改变而改变)


下一篇:POJ 3352 Road Construction 边的双连通分量 + 缩点