#include <rtapi_pci.h>
#include <rtapi_io.h>
#include "rtapi.h"
#include "rtapi_app.h"
#include "rtapi_string.h"
#include "hal.h"
#include "bitfile.h"
#include "hostmot2-lowlevel.h"
#include "hm2_pci.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sebastian Kuzminsky");
MODULE_DESCRIPTION("Driver for HostMot2 on the 5i2[012345], 6i25, 4i6[589], and 3x20 Anything I/O boards from Mesa Electronics");
MODULE_SUPPORTED_DEVICE("Mesa-AnythingIO-5i20");
static char *config[HM2_PCI_MAX_BOARDS];
RTAPI_MP_ARRAY_STRING(config, HM2_PCI_MAX_BOARDS, "config string for the AnyIO boards (see hostmot2(9) manpage)");
static int comp_id;
static hm2_pci_t hm2_pci_board[HM2_PCI_MAX_BOARDS];
static int num_boards = 0;
static int num_5i20 = 0;
static int num_5i21 = 0;
static int num_5i22 = 0;
static int num_5i23 = 0;
static int num_5i24 = 0;
static int num_5i25 = 0;
static int num_6i25 = 0;
static int num_4i65 = 0;
static int num_4i68 = 0;
static int num_4i69 = 0;
static int num_3x20 = 0;
static int failed_errno=0;
static struct rtapi_pci_device_id hm2_pci_tbl[] = {
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9030,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_5I20,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9054,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_5I21,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9030,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_4I65,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9054,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_5I22_10,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9054,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_5I22_15,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9054,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_5I23,
},
{
.vendor = HM2_PCI_VENDORID_MESA,
.device = HM2_PCI_DEV_MESA5I24,
.subvendor = HM2_PCI_VENDORID_MESA,
.subdevice = HM2_PCI_SSDEV_5I24,
},
{
.vendor = HM2_PCI_VENDORID_MESA,
.device = HM2_PCI_DEV_MESA5I25,
.subvendor = HM2_PCI_VENDORID_MESA,
.subdevice = HM2_PCI_SSDEV_5I25,
},
{
.vendor = HM2_PCI_VENDORID_MESA,
.device = HM2_PCI_DEV_MESA6I25,
.subvendor = HM2_PCI_VENDORID_MESA,
.subdevice = HM2_PCI_SSDEV_6I25,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9054,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_4I68_OLD,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9054,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_4I68,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9054,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_4I69_16,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9054,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_4I69_25,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9056,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_3X20_10,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9056,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_3X20_15,
},
{
.vendor = HM2_PCI_VENDORID_PLX,
.device = HM2_PCI_DEV_PLX9056,
.subvendor = HM2_PCI_VENDORID_PLX,
.subdevice = HM2_PCI_SSDEV_3X20_20,
},
{0,},
};
MODULE_DEVICE_TABLE(pci, hm2_pci_tbl);
static int hm2_pci_read(hm2_lowlevel_io_t *this, rtapi_u32 addr, void *buffer, int size) {
hm2_pci_t *board = this->private;
void *src = board->base + addr;
while (size > 0) {
*(rtapi_u32*)buffer = *(rtapi_u32*)src;
src += 4;
buffer += 4;
size -=4;
}
return 1; }
static int hm2_pci_write(hm2_lowlevel_io_t *this, rtapi_u32 addr, const void *buffer, int size) {
hm2_pci_t *board = this->private;
void *dest = board->base + addr;
while (size > 0) {
*(rtapi_u32*)dest = *(rtapi_u32*)buffer;
dest += 4;
buffer += 4;
size -=4;
}
return 1; }
static int hm2_plx9030_program_fpga(hm2_lowlevel_io_t *this, const bitfile_t *bitfile) {
hm2_pci_t *board = this->private;
int i;
rtapi_u32 status, control;
status = rtapi_inl(board->ctrl_base_addr + CTRL_STAT_OFFSET);
control = status & ~_WRITE_MASK & ~_LED_MASK;
rtapi_outl(control, board->ctrl_base_addr + CTRL_STAT_OFFSET);
for (i = 0; i < bitfile->e.size; i ++) {
rtapi_outb(bitfile_reverse_bits(bitfile->e.data[i]), board->data_base_addr);
}
status = rtapi_inl(board->ctrl_base_addr + CTRL_STAT_OFFSET);
if (!(status & _INIT_MASK)) {
THIS_ERR("FPGA asserted /INIT: CRC error\n");
goto fail;
}
if (!(status & DONE_MASK)) {
THIS_ERR("FPGA did not assert DONE\n");
goto fail;
}
control = status | _WRITE_MASK | _LED_MASK;
rtapi_outl(control, board->ctrl_base_addr + CTRL_STAT_OFFSET);
return 0;
fail:
status = rtapi_inl(board->ctrl_base_addr + CTRL_STAT_OFFSET);
control = status & ~_PROGRAM_MASK;
control |= _WRITE_MASK | _LED_MASK;
rtapi_outl(control, board->ctrl_base_addr + CTRL_STAT_OFFSET);
return -EIO;
}
static int hm2_plx9030_reset(hm2_lowlevel_io_t *this) {
hm2_pci_t *board = this->private;
rtapi_u32 status;
rtapi_u32 control;
status = rtapi_inl(board->ctrl_base_addr + CTRL_STAT_OFFSET);
control = status & ~_PROGRAM_MASK;
control |= _WRITE_MASK | _LED_MASK;
rtapi_outl(control, board->ctrl_base_addr + CTRL_STAT_OFFSET);
status = rtapi_inl(board->ctrl_base_addr + CTRL_STAT_OFFSET);
if (status & (DONE_MASK | _INIT_MASK)) {
THIS_ERR(
"FPGA did not reset: /INIT = %d, DONE = %d\n",
(status & _INIT_MASK ? 1 : 0),
(status & DONE_MASK ? 1 : 0)
);
return -EIO;
}
control = status | _PROGRAM_MASK;
rtapi_outl(control, board->ctrl_base_addr + CTRL_STAT_OFFSET);
{
int count = 3300;
do {
status = rtapi_inl(board->ctrl_base_addr + CTRL_STAT_OFFSET);
if (status & _INIT_MASK) break;
} while (count-- > 0);
if (count == 0) {
THIS_ERR("FPGA did not come out of /INIT\n");
return -EIO;
}
}
return 0;
}
static void hm2_plx9030_fixup_LASxBRD_READY(hm2_pci_t *board) {
hm2_lowlevel_io_t *this = &board->llio;
int offsets[] = { LAS0BRD_OFFSET, LAS1BRD_OFFSET, LAS2BRD_OFFSET, LAS3BRD_OFFSET };
int i;
for (i = 0; i < 4; i ++) {
rtapi_u32 val;
int addr = board->ctrl_base_addr + offsets[i];
val = rtapi_inl(addr);
if (!(val & LASxBRD_READY)) {
THIS_INFO("LAS%dBRD #READY is off, enabling now\n", i);
val |= LASxBRD_READY;
rtapi_outl(val, addr);
}
}
}
static int hm2_plx9054_program_fpga(hm2_lowlevel_io_t *this, const bitfile_t *bitfile) {
hm2_pci_t *board = this->private;
int i;
rtapi_u32 status;
for (i = 0; i < bitfile->e.size; i ++) {
rtapi_outb(bitfile_reverse_bits(bitfile->e.data[i]), board->data_base_addr);
}
for (i = 0; i < DONE_WAIT_5I22; i++) {
status = rtapi_inl(board->ctrl_base_addr + CTRL_STAT_OFFSET_5I22);
if (status & DONE_MASK_5I22) break;
}
if (i >= DONE_WAIT_5I22) {
THIS_ERR("Error: Not /DONE; programming not completed.\n");
return -EIO;
}
return 0;
}
static int hm2_plx9054_reset(hm2_lowlevel_io_t *this) {
hm2_pci_t *board = this->private;
int i;
rtapi_u32 status, control;
status = rtapi_inl(board->ctrl_base_addr + CTRL_STAT_OFFSET_5I22);
control = status | DONE_ENABLE_5I22 | _PROG_ENABLE_5I22;
rtapi_outl(control, board->ctrl_base_addr + CTRL_STAT_OFFSET_5I22);
rtapi_outl(control & ~_PROGRAM_MASK_5I22, board->ctrl_base_addr + CTRL_STAT_OFFSET_5I22);
status = rtapi_inl(board->ctrl_base_addr + CTRL_STAT_OFFSET_5I22);
if (status & DONE_MASK_5I22) {
THIS_ERR("/DONE status bit indicates busy at start of programming\n");
return -EIO;
}
rtapi_outl(control | _PROGRAM_MASK_5I22, board->ctrl_base_addr + CTRL_STAT_OFFSET_5I22);
for (i = 0; i < 3300; i++) {
status = rtapi_inl(board->ctrl_base_addr + CTRL_STAT_OFFSET);
}
return 0;
}
static int hm2_pci_probe(struct rtapi_pci_dev *dev, const struct rtapi_pci_device_id *id) {
int r;
hm2_pci_t *board;
hm2_lowlevel_io_t *this;
if (num_boards >= HM2_PCI_MAX_BOARDS) {
LL_PRINT("skipping AnyIO board at %s, this driver can only handle %d\n", rtapi_pci_name(dev), HM2_PCI_MAX_BOARDS);
return -EINVAL;
}
if (rtapi_pci_enable_device(dev)) {
LL_PRINT("skipping AnyIO board at %s, failed to enable PCI device\n", rtapi_pci_name(dev));
return failed_errno = -ENODEV;
}
board = &hm2_pci_board[num_boards];
this = &board->llio;
memset(this, 0, sizeof(hm2_lowlevel_io_t));
switch (dev->subsystem_device) {
case HM2_PCI_SSDEV_5I20: {
LL_PRINT("discovered 5i20 at %s\n", rtapi_pci_name(dev));
rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "hm2_5i20.%d", num_5i20);
num_5i20 ++;
board->llio.num_ioport_connectors = 3;
board->llio.pins_per_connector = 24;
board->llio.ioport_connector_name[0] = "P2";
board->llio.ioport_connector_name[1] = "P3";
board->llio.ioport_connector_name[2] = "P4";
board->llio.fpga_part_number = "2s200pq208";
board->llio.num_leds = 8;
break;
}
case HM2_PCI_SSDEV_5I21: {
LL_PRINT("discovered 5i21 at %s\n", rtapi_pci_name(dev));
rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "hm2_5i21.%d", num_5i21);
num_5i21 ++;
board->llio.num_ioport_connectors = 2;
board->llio.pins_per_connector = 32;
board->llio.ioport_connector_name[0] = "P1";
board->llio.ioport_connector_name[1] = "P1";
board->llio.fpga_part_number = "3s400pq208";
board->llio.num_leds = 8;
break;
}
case HM2_PCI_SSDEV_4I65: {
LL_PRINT("discovered 4i65 at %s\n", rtapi_pci_name(dev));
rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "hm2_4i65.%d", num_4i65);
num_4i65 ++;
board->llio.num_ioport_connectors = 3;
board->llio.pins_per_connector = 24;
board->llio.ioport_connector_name[0] = "P1";
board->llio.ioport_connector_name[1] = "P3";
board->llio.ioport_connector_name[2] = "P4";
board->llio.fpga_part_number = "2s200pq208";
board->llio.num_leds = 8;
break;
}
case HM2_PCI_SSDEV_5I22_10:
case HM2_PCI_SSDEV_5I22_15: {
if (dev->subsystem_device == HM2_PCI_SSDEV_5I22_10) {
LL_PRINT("discovered 5i22-1.0M at %s\n", rtapi_pci_name(dev));
board->llio.fpga_part_number = "3s1000fg320";
} else {
LL_PRINT("discovered 5i22-1.5M at %s\n", rtapi_pci_name(dev));
board->llio.fpga_part_number = "3s1500fg320";
}
rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "hm2_5i22.%d", num_5i22);
num_5i22 ++;
board->llio.num_ioport_connectors = 4;
board->llio.pins_per_connector = 24;
board->llio.ioport_connector_name[0] = "P2";
board->llio.ioport_connector_name[1] = "P3";
board->llio.ioport_connector_name[2] = "P4";
board->llio.ioport_connector_name[3] = "P5";
board->llio.num_leds = 8;
break;
}
case HM2_PCI_SSDEV_5I23: {
LL_PRINT("discovered 5i23 at %s\n", rtapi_pci_name(dev));
rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "hm2_5i23.%d", num_5i23);
num_5i23 ++;
board->llio.num_ioport_connectors = 3;
board->llio.pins_per_connector = 24;
board->llio.ioport_connector_name[0] = "P2";
board->llio.ioport_connector_name[1] = "P3";
board->llio.ioport_connector_name[2] = "P4";
board->llio.fpga_part_number = "3s400pq208";
board->llio.num_leds = 2;
break;
}
case HM2_PCI_SSDEV_5I24: {
LL_PRINT("discovered 5i24 at %s\n", rtapi_pci_name(dev));
rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "hm2_5i24.%d", num_5i24);
num_5i24 ++;
board->llio.num_ioport_connectors = 3;
board->llio.pins_per_connector = 24;
board->llio.ioport_connector_name[0] = "P4";
board->llio.ioport_connector_name[1] = "P3";
board->llio.ioport_connector_name[2] = "P2";
board->llio.fpga_part_number = "6slx16ftg256";
board->llio.num_leds = 2;
break;
}
case HM2_PCI_SSDEV_5I25:
case HM2_PCI_SSDEV_6I25: {
if (dev->subsystem_device == HM2_PCI_SSDEV_5I25) {
LL_PRINT("discovered 5i25 at %s\n", rtapi_pci_name(dev));
rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "hm2_5i25.%d", num_5i25);
num_5i25 ++;
} else {
LL_PRINT("discovered 6i25 at %s\n", rtapi_pci_name(dev));
rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "hm2_6i25.%d", num_6i25);
num_6i25 ++;
}
board->llio.num_ioport_connectors = 2;
board->llio.pins_per_connector = 17;
board->llio.ioport_connector_name[0] = "P3";
board->llio.ioport_connector_name[1] = "P2";
board->llio.fpga_part_number = "6slx9tqg144";
board->llio.num_leds = 2;
break;
}
case HM2_PCI_SSDEV_4I68:
case HM2_PCI_SSDEV_4I68_OLD: {
if (dev->subsystem_device == HM2_PCI_SSDEV_4I68_OLD) {
LL_PRINT("discovered OLD 4i68 at %s, please consider upgrading your EEPROM\n", rtapi_pci_name(dev));
} else {
LL_PRINT("discovered 4i68 at %s\n", rtapi_pci_name(dev));
}
rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "hm2_4i68.%d", num_4i68);
num_4i68 ++;
board->llio.num_ioport_connectors = 3;
board->llio.pins_per_connector = 24;
board->llio.ioport_connector_name[0] = "P1";
board->llio.ioport_connector_name[1] = "P2";
board->llio.ioport_connector_name[2] = "P4";
board->llio.fpga_part_number = "3s400pq208";
board->llio.num_leds = 4;
break;
}
case HM2_PCI_SSDEV_4I69_16:
case HM2_PCI_SSDEV_4I69_25: {
if (dev->subsystem_device == HM2_PCI_SSDEV_4I69_16) {
LL_PRINT("discovered 4I69-16 at %s\n", rtapi_pci_name(dev));
board->llio.fpga_part_number = "6slx16ftg256";
} else {
LL_PRINT("discovered 4I69-25 at %s\n", rtapi_pci_name(dev));
board->llio.fpga_part_number = "6slx25ftg256";
}
rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "hm2_4i69.%d", num_4i69);
num_4i69 ++;
board->llio.num_ioport_connectors = 3;
board->llio.pins_per_connector = 24;
board->llio.ioport_connector_name[0] = "P1";
board->llio.ioport_connector_name[1] = "P3";
board->llio.ioport_connector_name[2] = "P4";
break;
}
case HM2_PCI_SSDEV_3X20_10:
case HM2_PCI_SSDEV_3X20_15:
case HM2_PCI_SSDEV_3X20_20: {
if (dev->subsystem_device == HM2_PCI_SSDEV_3X20_10) {
LL_PRINT("discovered 3x20-1.0M at %s\n", rtapi_pci_name(dev));
board->llio.fpga_part_number = "3s1000fg456";
} else if (dev->subsystem_device == HM2_PCI_SSDEV_3X20_15) {
LL_PRINT("discovered 3x20-1.5M at %s\n", rtapi_pci_name(dev));
board->llio.fpga_part_number = "3s1500fg456";
} else {
LL_PRINT("discovered 3x20-2.0M at %s\n", rtapi_pci_name(dev));
board->llio.fpga_part_number = "3s2000fg456";
}
rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "hm2_3x20.%d", num_3x20);
num_3x20 ++;
board->llio.num_ioport_connectors = 6;
board->llio.pins_per_connector = 24;
board->llio.ioport_connector_name[0] = "P4";
board->llio.ioport_connector_name[1] = "P5";
board->llio.ioport_connector_name[2] = "P6";
board->llio.ioport_connector_name[3] = "P9";
board->llio.ioport_connector_name[4] = "P8";
board->llio.ioport_connector_name[5] = "P7";
board->llio.num_leds = 0;
break;
}
default: {
LL_ERR("unknown subsystem device id 0x%04x\n", dev->subsystem_device);
return failed_errno = -ENODEV;
}
}
switch (dev->device) {
case HM2_PCI_DEV_PLX9030: {
board->ctrl_base_addr = rtapi_pci_resource_start(dev, 1);
board->data_base_addr = rtapi_pci_resource_start(dev, 2);
board->len = rtapi_pci_resource_len(dev, dev->subsystem_device ? 5 : 3);
board->base = rtapi_pci_ioremap_bar(dev, dev->subsystem_device ? 5 : 3);
if (board->base == NULL) {
THIS_ERR("could not map in FPGA address space\n");
r = -ENODEV;
goto fail0;
}
hm2_plx9030_fixup_LASxBRD_READY(board);
board->llio.program_fpga = hm2_plx9030_program_fpga;
board->llio.reset = hm2_plx9030_reset;
break;
}
case HM2_PCI_DEV_PLX9056:
case HM2_PCI_DEV_PLX9054: {
board->ctrl_base_addr = rtapi_pci_resource_start(dev, 1);
board->data_base_addr = rtapi_pci_resource_start(dev, 2);
board->len = rtapi_pci_resource_len(dev, 3);
board->base = rtapi_pci_ioremap_bar(dev, 3);
if (board->base == NULL) {
THIS_ERR("could not map in FPGA address space\n");
r = -ENODEV;
goto fail0;
}
board->llio.program_fpga = hm2_plx9054_program_fpga;
board->llio.reset = hm2_plx9054_reset;
break;
}
case HM2_PCI_DEV_MESA5I24:
case HM2_PCI_DEV_MESA5I25:
case HM2_PCI_DEV_MESA6I25: {
board->len = rtapi_pci_resource_len(dev, 0);
board->base = rtapi_pci_ioremap_bar(dev, 0);
if (board->base == NULL) {
THIS_ERR("could not map in FPGA address space\n");
r = -ENODEV;
goto fail0;
}
break;
}
default: {
THIS_ERR("unknown PCI Device ID 0x%04x\n", dev->device);
r = -ENODEV;
goto fail0;
}
}
board->dev = dev;
rtapi_pci_set_drvdata(dev, board);
board->llio.comp_id = comp_id;
board->llio.private = board;
board->llio.threadsafe = 1;
board->llio.read = hm2_pci_read;
board->llio.write = hm2_pci_write;
r = hm2_register(&board->llio, config[num_boards]);
if (r != 0) {
THIS_ERR("board fails HM2 registration\n");
goto fail1;
}
THIS_PRINT("initialized AnyIO board at %s\n", rtapi_pci_name(dev));
num_boards ++;
return 0;
fail1:
rtapi_pci_set_drvdata(dev, NULL);
rtapi_iounmap(board->base);
board->base = NULL;
fail0:
rtapi_pci_disable_device(dev);
return failed_errno = r;
}
static void hm2_pci_remove(struct rtapi_pci_dev *dev) {
int i;
for (i = 0; i < num_boards; i++) {
hm2_pci_t *board = &hm2_pci_board[i];
hm2_lowlevel_io_t *this = &board->llio;
if (board->dev == dev) {
THIS_PRINT("dropping AnyIO board at %s\n", rtapi_pci_name(dev));
hm2_unregister(&board->llio);
if (board->base != NULL) {
rtapi_iounmap(board->base);
board->base = NULL;
}
rtapi_pci_disable_device(dev);
rtapi_pci_set_drvdata(dev, NULL);
board->dev = NULL;
}
}
}
static struct rtapi_pci_driver hm2_pci_driver = {
.name = HM2_LLIO_NAME,
.id_table = hm2_pci_tbl,
.probe = hm2_pci_probe,
.remove = hm2_pci_remove,
};
int rtapi_app_main(void) {
int r = 0;
LL_PRINT("loading Mesa AnyIO HostMot2 driver version " HM2_PCI_VERSION "\n");
comp_id = hal_init(HM2_LLIO_NAME);
if (comp_id < 0) return comp_id;
r = rtapi_pci_register_driver(&hm2_pci_driver);
if (r != 0) {
LL_ERR("error registering PCI driver\n");
hal_exit(comp_id);
return r;
}
if(failed_errno) {
hal_exit(comp_id);
rtapi_pci_unregister_driver(&hm2_pci_driver);
return failed_errno;
}
if(num_boards == 0) {
hal_exit(comp_id);
rtapi_pci_unregister_driver(&hm2_pci_driver);
return -ENODEV;
}
hal_ready(comp_id);
return 0;
}
void rtapi_app_exit(void) {
rtapi_pci_unregister_driver(&hm2_pci_driver);
LL_PRINT("driver unloaded\n");
hal_exit(comp_id);
}