// // Copyright (C) 2007-2008 Sebastian Kuzminsky // // 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // // // This driver behaves like a HostMot2 "low-level I/O" driver, but // provides unchanging, compiled-in, information. It runs without any // special hardware plugged in. Its job is to provide a test-pattern to // verify that the hostmot2 driver functions as it ought. // #include "rtapi.h" #include "rtapi_app.h" #include "rtapi_string.h" #include "hal.h" #include "hostmot2.h" #include "hostmot2-lowlevel.h" #include "hm2_test.h" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sebastian Kuzminsky"); MODULE_DESCRIPTION("Test pattern for the hostmot2 driver, does not talk to any hardware"); static char *config[HM2_TEST_MAX_BOARDS]; RTAPI_MP_ARRAY_STRING(config, HM2_TEST_MAX_BOARDS, "config string for the AnyIO boards (see hostmot2(9) manpage)"); int test_pattern = 0; RTAPI_MP_INT(test_pattern, "The test pattern to show to the hostmot2 driver."); static int comp_id; static hm2_test_t board[1]; // // Functions for initializing the register file on the pretend hm2 board. // static void set8(hm2_test_t *me, uint16_t addr, uint8_t val) { me->test_pattern.tp8[addr] = val; } static void set32(hm2_test_t *me, uint16_t addr, uint32_t val) { me->test_pattern.tp32[addr/4] = val; } // // these are the "low-level I/O" functions exported up // static int hm2_test_read(hm2_lowlevel_io_t *this, rtapi_u32 addr, void *buffer, int size) { hm2_test_t *me = this->private; memcpy(buffer, &me->test_pattern.tp8[addr], size); return 1; // success } static int hm2_test_write(hm2_lowlevel_io_t *this, rtapi_u32 addr, const void *buffer, int size) { return 1; // success } static int hm2_test_program_fpga(hm2_lowlevel_io_t *this, const bitfile_t *bitfile) { return 0; } static int hm2_test_reset(hm2_lowlevel_io_t *this) { return 0; } int rtapi_app_main(void) { hm2_test_t *me; hm2_lowlevel_io_t *this; int r = 0; LL_ERR("loading HostMot2 test driver with test pattern %d\n", test_pattern); comp_id = hal_init(HM2_LLIO_NAME); if (comp_id < 0) return comp_id; me = &board[0]; this = &me->llio; memset(this, 0, sizeof(hm2_lowlevel_io_t)); me->llio.num_ioport_connectors = 1; me->llio.pins_per_connector = 24; me->llio.ioport_connector_name[0] = "P99"; switch (test_pattern) { // // this one has nothing // case 0: { break; } // // this one has a good IO Cookie, but that's it // case 1: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); break; } // // this one has a good IO Cookie and Config Name // the idrom offset is 0, and there's nothing there // case 2: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); break; } // // good IO Cookie, Config Name, and IDROM Type // the IDROM offset is the usual, 0x400, and there's an invalid IDROM type there // case 3: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); // put the IDROM at 0x400, where it usually lives set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // bad idrom type set32(me, 0x400, 0x1234); break; } // // good IO Cookie, Config Name, and IDROM Type // the IDROM offset is the usual, 0x400, and there's a good IDROM type there // but the portwidth is 0 // case 4: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // put the IDROM at 0x400, where it usually lives set32(me, 0x400, 2); // standard idrom type break; } // // good IO Cookie, Config Name, and IDROM Type // the IDROM offset is the usual, 0x400, and there's a good IDROM type there // but the portwidth is 29 which is bogus // case 5: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // put the IDROM at 0x400, where it usually lives set32(me, 0x400, 2); // standard idrom type // bad PortWidth set32(me, 0x424, 29); break; } // // good IO Cookie, Config Name, and IDROM Type // the IDROM offset is the usual, 0x400, and there's a good IDROM type there // good PortWidth // case 6: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // put the IDROM at 0x400, where it usually lives set32(me, 0x400, 2); // standard idrom type // good PortWidth set32(me, 0x424, 24); break; } // // good IO Cookie, Config Name, and IDROM Type // the IDROM offset is the usual, 0x400, and there's a good IDROM type there // good PortWidth, but problematic IOPorts and IOWidth // case 7: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // put the IDROM at 0x400, where it usually lives set32(me, 0x400, 2); // standard idrom type // good PortWidth = 24, which is standard set32(me, 0x424, 24); // IOPorts = 1 set32(me, 0x41c, 1); // IOWidth = 99 (!= IOPorts * PortWidth) set32(me, 0x420, 99); break; } // // good IO Cookie, Config Name, and IDROM Type // the IDROM offset is the usual, 0x400, and there's a good IDROM type there // good PortWidth, but IOPorts doesn't match what the llio said // case 8: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // put the IDROM at 0x400, where it usually lives set32(me, 0x400, 2); // standard idrom type set32(me, 0x424, 24); // PortWidth = 24 // IOPorts = 2 (!= what the llio said) set32(me, 0x41c, 2); // IOWidth == IOPorts * PortWidth) set32(me, 0x420, 48); break; } // // good IO Cookie, Config Name, and IDROM Type // the IDROM offset is the usual, 0x400, and there's a good IDROM type there // good PortWidth, IOPorts, and IOWidth // but the clocks are bad // case 9: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // put the IDROM at 0x400, where it usually lives set32(me, 0x400, 2); // standard idrom type set32(me, 0x424, 24); // PortWidth = 24 // IOWidth = (IOPorts * PortWidth) set32(me, 0x41c, 1); set32(me, 0x420, 24); // ClockLow = 12345 set32(me, 0x428, 12345); break; } // // good IO Cookie, Config Name, and IDROM Type // the IDROM offset is the usual, 0x400, and there's a good IDROM type there // good PortWidth, IOPorts, and IOWidth // but the clocks are bad // case 10: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // put the IDROM at 0x400, where it usually lives set32(me, 0x400, 2); // standard idrom type set32(me, 0x41c, 1); // IOPorts = 1 set32(me, 0x420, 24); // IOWidth = (IOPorts * PortWidth) set32(me, 0x424, 24); // PortWidth = 24 // ClockLow = 2e6 set32(me, 0x428, 2e6); // ClockHigh = 0 set32(me, 0x42c, 0); break; } // // good IO Cookie, Config Name, and IDROM Type // the IDROM offset is the usual, 0x400, and there's a good IDROM type there // good PortWidth, IOPorts, IOWidth, and clocks // // The problem with this register file is that the Pin Descriptor // array contains no valid PDs, though the IDROM advertised 144 pins. // case 11: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // put the IDROM at 0x400, where it usually lives set32(me, 0x400, 2); // standard idrom type // normal offset to Module Descriptors set32(me, 0x404, 64); // unusual offset to PinDescriptors set32(me, 0x408, 0x1C0); // IOPorts set32(me, 0x41c, 6); // IOWidth set32(me, 0x420, 6*24); // PortWidth set32(me, 0x424, 24); // ClockLow = 2e6 set32(me, 0x428, 2e6); // ClockHigh = 2e7 set32(me, 0x42c, 2e7); me->llio.num_ioport_connectors = 6; me->llio.ioport_connector_name[0] = "P4"; me->llio.ioport_connector_name[1] = "P5"; me->llio.ioport_connector_name[2] = "P6"; me->llio.ioport_connector_name[3] = "P9"; me->llio.ioport_connector_name[4] = "P8"; me->llio.ioport_connector_name[5] = "P7"; break; } // // good IO Cookie, Config Name, and IDROM Type // the IDROM offset is the usual, 0x400, and there's a good IDROM type there // good PortWidth, IOWidth, and clocks // but there are no IOPorts instances according to the MDs // (this is the case with a firmware Jeff made for testing an RNG circuit) // case 12: { int num_io_pins = 24; int pd_index; set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // put the IDROM at 0x400, where it usually lives set32(me, 0x400, 2); // standard idrom type // normal offset to Module Descriptors set32(me, 0x404, 64); // normal offset to PinDescriptors set32(me, 0x408, 0x200); // IOPorts set32(me, 0x41c, 1); // IOWidth set32(me, 0x420, num_io_pins); // PortWidth set32(me, 0x424, 24); // ClockLow = 2e6 set32(me, 0x428, 2e6); // ClockHigh = 2e7 set32(me, 0x42c, 2e7); me->llio.num_ioport_connectors = 1; me->llio.ioport_connector_name[0] = "P3"; // make a bunch of valid Pin Descriptors for (pd_index = 0; pd_index < num_io_pins; pd_index ++) { set8(me, 0x600 + (pd_index * 4) + 0, 0); // SecPin (byte) = Which pin of secondary function connects here eg: A,B,IDX. Output pins have bit 7 = '1' set8(me, 0x600 + (pd_index * 4) + 1, 0); // SecTag (byte) = Secondary function type (PWM,QCTR etc). Same as module GTag set8(me, 0x600 + (pd_index * 4) + 2, 0); // SecUnit (byte) = Which secondary unit or channel connects here set8(me, 0x600 + (pd_index * 4) + 3, HM2_GTAG_IOPORT); // PrimaryTag (byte) = Primary function tag (normally I/O port) } break; } // this board has a non-standard (ie, non-24) number of pins per connector, but the idrom does not match that case 13: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // put the IDROM at 0x400, where it usually lives set32(me, 0x400, 2); // standard idrom type // default PortWidth set32(me, 0x424, 24); // unusual number of pins per connector me->llio.pins_per_connector = 5; break; } // // good IO Cookie, Config Name, and IDROM Type // the IDROM offset is the usual, 0x400, and there's a good IDROM type there // good but unusual (non-24) PortWidth // case 14: { set32(me, HM2_ADDR_IOCOOKIE, HM2_IOCOOKIE); set8(me, HM2_ADDR_CONFIGNAME+0, 'H'); set8(me, HM2_ADDR_CONFIGNAME+1, 'O'); set8(me, HM2_ADDR_CONFIGNAME+2, 'S'); set8(me, HM2_ADDR_CONFIGNAME+3, 'T'); set8(me, HM2_ADDR_CONFIGNAME+4, 'M'); set8(me, HM2_ADDR_CONFIGNAME+5, 'O'); set8(me, HM2_ADDR_CONFIGNAME+6, 'T'); set8(me, HM2_ADDR_CONFIGNAME+7, '2'); set32(me, HM2_ADDR_IDROM_OFFSET, 0x400); // put the IDROM at 0x400, where it usually lives set32(me, 0x400, 2); // standard idrom type // good but unusual PortWidth set32(me, 0x424, 37); me->llio.pins_per_connector = 37; break; } default: { LL_ERR("unknown test pattern %d", test_pattern); return -ENODEV; } } rtapi_snprintf(me->llio.name, sizeof(me->llio.name), "hm2_test.0"); me->llio.fpga_part_number = "none"; me->llio.program_fpga = hm2_test_program_fpga; me->llio.reset = hm2_test_reset; me->llio.comp_id = comp_id; me->llio.private = me; me->llio.threadsafe = 1; me->llio.read = hm2_test_read; me->llio.write = hm2_test_write; r = hm2_register(&board->llio, config[0]); if (r != 0) { THIS_ERR("hm2_test fails HM2 registration\n"); return -EIO; } THIS_PRINT("initialized hm2 test-pattern %d\n", test_pattern); hal_ready(comp_id); return 0; } void rtapi_app_exit(void) { hm2_test_t *me = &board[0]; hm2_unregister(&me->llio); LL_PRINT("driver unloaded\n"); hal_exit(comp_id); }