ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

xenomai驱动开发-PCI驱动开发-1

2022-07-24 12:03:49  阅读:175  来源: 互联网

标签:rtcan PCI dev pci board xenomai 驱动 peak


1.参考文档
网 址:http://en.wikipedia.org/wiki/Conventional_PCI ;
网 址:http://en.wikipedia.org/wiki/PCI_configuration_space ;
2.配置与初始化

在系统启动时,在 x86 上,BIOS 负责配置 PCI 设备。在其他平台上,Linux内核可以完成这项工作。但是,无论硬件目标如何,当您到达初始化Linux / Xenomai驱动程序时,PCI设备已经“配置”,您不必这样做。“配置”的PCI设备意味着其内存地址范围已被保留,其IRQ已分配给它等;设备尚未初始化!
初始化确实特定于每个设备,这是驱动程序的角色。Xenomai PCI驱动程序将使用Linux来发现设备的配置,检索其IRQ以及它使用的IO或内存的地址范围。我们不会尝试通过我们的驱动程序使Linux与此设备进行通信。Linux在这里被用作“发现”PCI总线的一种方式,仅此而已。
使用 Linux PCI API 和适当的 IO/内存操作函数可以创建跨平台 PCI 驱动程序。

3.逐步编写驱动程序
Xenomai PCI 驱动程序必须:

点击查看代码
static struct pci_device_id peak_pci_tbl[] = {
      {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
      { }
};
MODULE_DEVICE_TABLE (pci, peak_pci_tbl);
 与大多数Linux“驱动程序层”一样,还需要注册将调用以驱动我们的PCI设备的回调。例如,tty,网卡,USB等就是这种情况。在这里,我们只关注PCI和Xenomai驱动程序。
 因此,我们将仅使用Linux提供的最低限度:Linux在发现PCI设备或系统关闭时调用的“probe”和“remove”函数。其他回调与我们无关,我们无论如何都不想通过Linux直接访问硬件。      具体来说,在代码中,它给出了:
点击查看代码
static struct pci_driver rtcan_peak_pci_driver = {
      .name     = RTCAN_DRV_NAME,
      .id_table = peak_pci_tbl,
      .probe    = peak_pci_init_one,
      .remove   = __devexit_p(peak_pci_remove_one),
};
 
static int __init rtcan_peak_pci_init(void)
{
    return pci_register_driver(&rtcan_peak_pci_driver);
}

pci_read_config_word(pdev, 0x2e, &sub_sys_id)
另一个示例来自 Essential Linux Device Drivers(42)一书,用于读取与该卡关联的 IRQ:
点击查看代码
unsigned char irq ;
pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq);
 因此,您不需要知道配置区域中的哪一个字节表示 BIOS 分配的 IRQ,在 include/linux/pci_regs.h 中找到的宏PCI_INTERRUPT_LINE就足够了。有关 PCI 卡配置区域中可恢复的宏和值的详细信息,请参阅此文件。
 
每个PCI设备最多有六个“基址寄存器”或“BAR”。这是什么?
简而言之,在64字节的配置中,6 * 4字节表示PCI卡将使用的内存区域。这六个地址范围的使用由卡制造商自行决定。值得参考卡的配置,以了解使用了多少个这些“BAR”。
在这些“BAR”中,一些是指IO地址,另一些是指内存地址;同样,是map的文档表明了这一点。
或联系制造商。
或者通过从Linux源代码“改造”。
**********************************************************************
要开发 PCI 驱动程序,您必须具有卡的“开发人员”文档。有两种方法可以获得它。
理想的情况仍然是两者兼而有之(理论和实践都有效)。
但是,一些制造商不一定玩游戏,只在他们的卡上提供Linux模块(.ko文件)。
因此,在选择  材料时,这必须特别注意。
还应该注意的是,一些制造商单独出售其Linux驱动程序的代码,或者在签署保密协议(43)(例如TEWS Technologies)的情况下免费提供它们。
******************************************

Linux在其PCI API中提供了一些函数,以了解是否定义了“BAR”以及它所引用的地址范围。因此,使用这些Linux功能,可以在驱动程序的初始化阶段检索我们感兴趣的地址范围,从而能够读取/写入来驱动我们的硬件。您可以引用函数 pci_resource_[start|len|end|flags] 这些函数将 Linux 传递的“PCI 描述符”作为第一个参数,将 bar 编号作为第二个参数。
例:
点击查看代码
unsigned long addr;
addr = pci_resource_start(pdev, 0);
注意: 32 位 PCI 总线处理 32 位地址,因此通常使用“无符号长整型”而不是“指针”,后者的大小因处理器的地址总线而异。“Long”由ANSI C标准在32位上定义,并且不因编译器/平台而异。因此,代码是可移植的。

对设备的读/写访问在 IO(PIO 访问方法)中完成,或直接在内存映射区域(MMIO 访问方法)中完成。是设备的文档提供了此信息。在驱动程序编程方面,唯一改变的是需要使用的API。
点击查看代码

    ·     io_base = pci_ressouce_start(my_pci_dev, 0);//这给出了 BAR0 的基本地址

    ·     request_region(io_base, 1024, “my_driver_xenomai_pci”); //我们向 Linux 宣布,我们将使用 BAR0 基址中的 1024 个字节。在这里,假设设备的文档为我们提供了此信息。此功能主要用于检查其他驱动程序是否尚未访问此区域。在这种情况下,存在冲突(重叠),应返回错误代码。

    ·     data = inb(io_base + 5);//我们访问 IO BAR0 区域的第 6 个字节。我们可以用同样的方式编写:outb(0x01,iobase+5) è 我们在IO BAR0区域的第6个字节中写入0x01。

    ·     io_base = pci_ressouce_start(my_pci_dev, 0);//这给出了 BAR0 的基址。

    ·     io_longueur = pci_resource_length(my_pci_dev, 0);//我们获取 bar0 内存区域的宣布长度。在这里,信息直接在PCI配置中找到。不使用设备文档。

    ·     request_mem_region(io_base, io_longueur, “my_driver_xenomai_pci”);//我们宣布 Linux,我们将使用从基址“io_base”开始的内存区域,长度为“io_longueur”字节。此功能主要用于检查其他驱动程序是否尚未访问此区域。在这种情况下,存在冲突,建议返回错误代码。
注意:pci_request_region是Linux提供的一个功能,允许您同时执行上述三个步骤。

·您将能够在驱动程序中访问的“内核”内存地址与 BAR0 找到的值不同,这是很正常的。实际上,内核使用虚拟地址,并且正是 MMU 使转换成为“内核地址到总线地址”。要获得“总线”地址的“内核”等效物,我们必须使用 ioremap() 函数。

示例: adresse_noyau = ioremap(io_base, io_longueur);

有时,这些与 PCI 设备相对应的内存映射区域不支持由处理器进行缓存。实际上,在访问内存总线(MMIO方法)时,处理器可以将此数据放在其内部缓存中,以加快下次访问时获取数据的速度。但是这个“内存”直接由设备控制,并且可以在设备更新时自发地更改其值。因此,处理器缓存可能包含一个过时的值,该值将在下次读取 [b|l|..] 中返回。

ioremap_nocache() 是与上述 ioremap() 相同的函数,但确保返回的内核地址不会被 CPU 缓存。

注 1:pci_resource_flag() 函数提供对此类信息的访问,以了解处理器是否可以缓存“BAR”区域。

注 2:参见 lib/iomap.c 了解 pci_iomap() 函数的使用,该函数在单个操作中对到目前为止描述的内容进行分组。

· data = readb(adresse_noyau+5)// 我们访问 BAR0 内存区域的第 6 个字节。

我们可以用同样的方式写:

writeb(0x01,adresse_noyau +5) //我们在内存盒BAR0的第6个字节中写0x01。

提示:关于驱动程序对IO或内存的“保留”,您可以通过/proc/ioports和/proc/iomem来观察它们。

以下是Xenomai提供的CAN SJA1000驱动程序的示例:rtan_peak_pci.c。这里重点介绍了上面提到的功能。此简洁的驱动程序允许您查找实现 Xenomai PCI 驱动程序所需的步骤。在此驱动程序中,访问方法是 MMIO。
此代码仅包含“可见”PCI 部分,卡的控制使用其他源文件。
点击查看代码
/*
 * Copyright (C) 2006 Wolfgang Grandegger <wg@grandegger.com>
 *
 * Derived from the PCAN project file driver/src/pcan_pci.c:
 *
 * Copyright (C) 2001-2006  PEAK System-Technik GmbH
 *
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
 
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <asm/io.h>
 
#include <rtdm/rtdm_driver.h>
 
/* CAN device profile */
#include <rtdm/rtcan.h>
#include <rtcan_dev.h>
#include <rtcan_raw.h>
#include <rtcan_sja1000.h>
#include <rtcan_sja1000_regs.h>
 
#define RTCAN_DEV_NAME    "rtcan%d"
#define RTCAN_DRV_NAME    "PEAK-PCI-CAN"
 
static char *peak_pci_board_name = "PEAK-PCI";
 
MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
MODULE_DESCRIPTION("RTCAN board driver for PEAK-PCI cards");
MODULE_SUPPORTED_DEVICE("PEAK-PCI card CAN controller");
MODULE_LICENSE("GPL");
 
struct rtcan_peak_pci
{
    struct pci_dev *pci_dev; 
    struct rtcan_device *slave_dev;
    int channel;
    volatile void __iomem *base_addr;  
    volatile void __iomem *conf_addr;
};
 
#define PEAK_PCI_CAN_SYS_CLOCK (16000000 / 2)
 
#define PELICAN_SINGLE  (SJA_CDR_CAN_MODE | SJA_CDR_CBP | 0x07 | SJA_CDR_CLK_OFF)
#define PELICAN_MASTER  (SJA_CDR_CAN_MODE | SJA_CDR_CBP | 0x07            )
#define PELICAN_DEFAULT (SJA_CDR_CAN_MODE                                 )
 
#define CHANNEL_SINGLE 0 /* this is a single channel device */
#define CHANNEL_MASTER 1 /* multi channel device, this device is master */
#define CHANNEL_SLAVE  2 /* multi channel device, this is slave */
 
// important PITA registers
#define PITA_ICR         0x00        // interrupt control register
#define PITA_GPIOICR     0x18        // general purpose IO interface control register
#define PITA_MISC        0x1C        // miscellanoes register
 
#define PEAK_PCI_VENDOR_ID   0x001C  // the PCI device and vendor IDs
#define PEAK_PCI_DEVICE_ID   0x0001
 
#define PCI_CONFIG_PORT_SIZE 0x1000  // size of the config io-memory
#define PCI_PORT_SIZE        0x0400  // size of a channel io-memory
 
static struct pci_device_id peak_pci_tbl[] = {
      {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
      { }
};
MODULE_DEVICE_TABLE (pci, peak_pci_tbl);
 
 
static u8 rtcan_peak_pci_read_reg(struct rtcan_device *dev, int port)
{
    struct rtcan_peak_pci *board = (struct rtcan_peak_pci *)dev->board_priv;
    return readb(board->base_addr + ((unsigned long)port << 2));
}
 
static void rtcan_peak_pci_write_reg(struct rtcan_device *dev, int port, u8 data)
{
    struct rtcan_peak_pci *board = (struct rtcan_peak_pci *)dev->board_priv;
    writeb(data, board->base_addr + ((unsigned long)port << 2));
}
 
static void rtcan_peak_pci_irq_ack(struct rtcan_device *dev)
{
    struct rtcan_peak_pci *board = (struct rtcan_peak_pci *)dev->board_priv;
    u16 pita_icr_low;
 
    /* Select and clear in Pita stored interrupt */
    pita_icr_low = readw(board->conf_addr + PITA_ICR);
    if (board->channel == CHANNEL_SLAVE) {
      if (pita_icr_low & 0x0001)
          writew(0x0001, board->conf_addr + PITA_ICR);
    } 
    else {
      if (pita_icr_low & 0x0002)
          writew(0x0002, board->conf_addr + PITA_ICR);
    }
}
 
static void rtcan_peak_pci_del_chan(struct rtcan_device *dev, 
                            int init_step)
{
    struct rtcan_peak_pci *board;
    u16 pita_icr_high;
 
    if (!dev)
      return;
 
    board = (struct rtcan_peak_pci *)dev->board_priv;
 
    switch (init_step) {
    case 0:             /* Full cleanup */
      printk("Removing %s %s device %s\n", 
             peak_pci_board_name, dev->ctrl_name, dev->name);
      rtcan_sja1000_unregister(dev);
    case 5: 
      pita_icr_high = readw(board->conf_addr + PITA_ICR + 2);
      if (board->channel == CHANNEL_SLAVE) {
          pita_icr_high &= ~0x0001;
      } else {
          pita_icr_high &= ~0x0002;
      }
      writew(pita_icr_high, board->conf_addr + PITA_ICR + 2); 
    case 4:
      iounmap((void *)board->base_addr);
    case 3:
      if (board->channel != CHANNEL_SLAVE)
          iounmap((void *)board->conf_addr);
    case 2:
        rtcan_dev_free(dev);    
    case 1:
      break;
    }
 
}
 
static int rtcan_peak_pci_add_chan(struct pci_dev *pdev, int channel, 
                           struct rtcan_device **master_dev)
{
    struct rtcan_device *dev;
    struct rtcan_sja1000 *chip;
    struct rtcan_peak_pci *board;
    u16 pita_icr_high;
    unsigned long addr;
    int ret, init_step = 1;
 
    dev = rtcan_dev_alloc(sizeof(struct rtcan_sja1000),
                    sizeof(struct rtcan_peak_pci));
    if (dev == NULL)
        return -ENOMEM;
    init_step = 2;
    
    chip = (struct rtcan_sja1000 *)dev->priv;
    board = (struct rtcan_peak_pci *)dev->board_priv;
 
    board->pci_dev = pdev;
    board->channel = channel;
 
    if (channel != CHANNEL_SLAVE) {
 
      addr = pci_resource_start(pdev, 0);    
      board->conf_addr = ioremap(addr, PCI_CONFIG_PORT_SIZE); 
      if (board->conf_addr == 0) {
          ret = -ENODEV;
          goto failure;
      }
      init_step = 3;
    
      /* Set GPIO control register */
      writew(0x0005, board->conf_addr + PITA_GPIOICR + 2);  
    
      if (channel == CHANNEL_MASTER)
          writeb(0x00, board->conf_addr + PITA_GPIOICR); /* enable both */
      else
          writeb(0x04, board->conf_addr + PITA_GPIOICR); /* enable single */
      
      writeb(0x05, board->conf_addr + PITA_MISC + 3);  /* toggle reset */
      mdelay(5);
      writeb(0x04, board->conf_addr + PITA_MISC + 3);  /* leave parport mux mode */           
    } else {
      struct rtcan_peak_pci *master_board = 
          (struct rtcan_peak_pci *)(*master_dev)->board_priv;
      master_board->slave_dev = dev;
      board->conf_addr = master_board->conf_addr;
    }
 
    addr = pci_resource_start(pdev, 1);    
    if (channel == CHANNEL_SLAVE)
      addr += 0x400;
    
    board->base_addr = ioremap(addr, PCI_PORT_SIZE);
    if (board->base_addr == 0) {
      ret = -ENODEV;
      goto failure;
    }
    init_step = 4;
 
    dev->board_name = peak_pci_board_name;
 
    chip->read_reg = rtcan_peak_pci_read_reg;
    chip->write_reg = rtcan_peak_pci_write_reg;
    chip->irq_ack = rtcan_peak_pci_irq_ack;
 
    /* Clock frequency in Hz */
    dev->can_sys_clock = PEAK_PCI_CAN_SYS_CLOCK;
 
    /* Output control register */
    chip->ocr = SJA_OCR_MODE_NORMAL | SJA_OCR_TX0_PUSHPULL;
 
    /* Clock divider register */
    if (channel == CHANNEL_MASTER)
      chip->cdr = PELICAN_MASTER;
    else
      chip->cdr = PELICAN_SINGLE;
 
    strncpy(dev->name, RTCAN_DEV_NAME, IFNAMSIZ);
 
    /* Register and setup interrupt handling */
    chip->irq_flags = RTDM_IRQTYPE_SHARED;
    chip->irq_num = pdev->irq;
    pita_icr_high = readw(board->conf_addr + PITA_ICR + 2);
    if (channel == CHANNEL_SLAVE) {
      pita_icr_high |= 0x0001;
    } else {
      pita_icr_high |= 0x0002;
    }
    writew(pita_icr_high, board->conf_addr + PITA_ICR + 2); 
    init_step = 5;
      
    printk("%s: base_addr=%p conf_addr=%p irq=%d\n", RTCAN_DRV_NAME, 
         board->base_addr, board->conf_addr, chip->irq_num);
 
    /* Register SJA1000 device */
    ret = rtcan_sja1000_register(dev);
    if (ret) {
      printk(KERN_ERR
             "ERROR %d while trying to register SJA1000 device!\n", ret);
      goto failure;
    }
 
    if (channel != CHANNEL_SLAVE)
      *master_dev = dev;
 
    return 0;
 
 failure:
    rtcan_peak_pci_del_chan(dev, init_step);
    return ret;
}
 
static int __devinit peak_pci_init_one (struct pci_dev *pdev,
                              const struct pci_device_id *ent)
{
    int ret;
    u16 sub_sys_id;
    struct rtcan_device *master_dev = NULL;
 
    printk("%s: initializing device %04x:%04x\n",
         RTCAN_DRV_NAME,  pdev->vendor, pdev->device);
 
    if ((ret = pci_enable_device (pdev)))
      goto failure;
 
    if ((ret = pci_request_regions(pdev, RTCAN_DRV_NAME)))
      goto failure;
 
    if ((ret = pci_read_config_word(pdev, 0x2e, &sub_sys_id)))
      goto failure_cleanup;
    
    /* Enable memory space */
    if ((ret = pci_write_config_word(pdev, 0x04, 2)))
      goto failure_cleanup;
    
    if ((ret = pci_write_config_word(pdev, 0x44, 0)))
      goto failure_cleanup;
    
    if (sub_sys_id > 3) {
      if ((ret = rtcan_peak_pci_add_chan(pdev, CHANNEL_MASTER, 
                                 &master_dev)))
          goto failure_cleanup;
      if ((ret = rtcan_peak_pci_add_chan(pdev, CHANNEL_SLAVE, 
                                 &master_dev)))
          goto failure_cleanup;
    } else {
      if ((ret = rtcan_peak_pci_add_chan(pdev, CHANNEL_SINGLE,
                                 &master_dev)))
          goto failure_cleanup;
    }
 
    pci_set_drvdata(pdev, master_dev);
    return 0;
 
 failure_cleanup:
    if (master_dev)
      rtcan_peak_pci_del_chan(master_dev, 0);
 
    pci_release_regions(pdev);
    
 failure:
    return ret;
      
}
 
static void __devexit peak_pci_remove_one (struct pci_dev *pdev)
{
    struct rtcan_device *dev = pci_get_drvdata(pdev);
    struct rtcan_peak_pci *board = (struct rtcan_peak_pci *)dev->board_priv;
 
    if (board->slave_dev)
      rtcan_peak_pci_del_chan(board->slave_dev, 0);
    rtcan_peak_pci_del_chan(dev, 0);
 
    pci_release_regions(pdev);
    pci_disable_device(pdev);
    pci_set_drvdata(pdev, NULL);
}
 
static struct pci_driver rtcan_peak_pci_driver = {
      .name       = RTCAN_DRV_NAME,
      .id_table   = peak_pci_tbl,
      .probe            = peak_pci_init_one,
      .remove           = __devexit_p(peak_pci_remove_one),
};
 
static int __init rtcan_peak_pci_init(void)
{
    return pci_register_driver(&rtcan_peak_pci_driver);
}
 
 
static void __exit rtcan_peak_pci_exit(void)
{
    pci_unregister_driver(&rtcan_peak_pci_driver);
}
 
module_init(rtcan_peak_pci_init);
module_exit(rtcan_peak_pci_exit);

标签:rtcan,PCI,dev,pci,board,xenomai,驱动,peak
来源: https://www.cnblogs.com/wangjirui/p/16514201.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有