Ignore:
Timestamp:
Feb 22, 2011, 2:25:44 AM (14 years ago)
Author:
chris
Message:
  • Further fixes to automatic ATAPI sense handling, now supporting sense buffers larger than 64 bytes if requested by initiator (cdrecord wanted 96 bytes)
  • Separate, and internally handled, spinlock for libc malloc/free calls to reduce chances of memory corruption if somebody forgets to get the driver-level spinlock before calling malloc/free. There was no real problem with that, just some awkward code fragments which look much better now.
  • Link power management implemented
  • More generic support for adapter/port options so all of them can now have a global, adapter or port scope
  • Generic support for inverting driver options (i.e. turn them off with '!')
  • Thorough PCI scan is now the default; the reason it wasn't so far was a delay in Virtualbox but that was never a problem on real hardware
  • SCSI emulation for ATAPI devices; this can be enabled on global, adapter or port scope
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/os2ahci/os2ahci.c

    r76 r77  
    4141  }
    4242
     43/* set two-dimensional array of port options */
     44#define set_port_option(opt, val)                         \
     45  if (adapter_index == -1) {                              \
     46    /* set option for all adapters and ports */           \
     47    memset(opt, val, sizeof(opt));                        \
     48  } else if (port_index == -1) {                          \
     49    /* set option for all ports on current adapter */     \
     50    memset(opt[adapter_index], val, sizeof(*opt));        \
     51  } else {                                                \
     52    /* set option for specific port */                    \
     53    opt[adapter_index][port_index] = val;                 \
     54  }
     55
    4356/* ------------------------ typedefs and structures ------------------------ */
    4457
    4558/* -------------------------- function prototypes -------------------------- */
    4659
    47 void     _cdecl small_code_          (void);
     60void _cdecl  small_code_      (void);
     61
     62static int   add_unit_info    (IORB_CONFIGURATION _far *iorb_conf, int dt_ai,
     63                               int a, int p, int d, int scsi_id);
    4864
    4965/* ------------------------ global/static variables ------------------------ */
    5066
    5167int             debug = 0;         /* if > 0, print debug messages to COM1 */
    52 int             thorough_scan;    /* if != 0, perform thorough PCI scan */
     68int             thorough_scan = 1; /* if != 0, perform thorough PCI scan */
    5369int             init_reset;        /* if != 0, reset ports during init */
    5470
     
    84100
    85101/* apapter/port-specific options saved when parsing the command line */
     102u8              emulate_scsi[MAX_AD][AHCI_MAX_PORTS];
     103u8              disable_ncq[MAX_AD][AHCI_MAX_PORTS];
    86104u8              link_speed[MAX_AD][AHCI_MAX_PORTS];
    87 u8              disable_ncq[MAX_AD][AHCI_MAX_PORTS];
     105u8              link_power[MAX_AD][AHCI_MAX_PORTS];
    88106
    89107static char     init_msg[] = "OS2AHCI driver version %d.%02d\n";
     
    91109static char     eval_msg[] = ANSI_CLR_RED ANSI_CLR_BRIGHT "Evaluation version "
    92110                             "- not licensed for production use.\n" ANSI_RESET;
    93 
    94111
    95112/* ----------------------------- start of code ----------------------------- */
     
    139156  int adapter_index = -1;
    140157  int port_index = -1;
     158  int invert_option;
     159  int optval;
    141160  u16 vendor;
    142161  u16 device;
     
    148167  DevHelp_CreateSpinLock(&drv_lock);
    149168
    150   if (debug) {
    151     /* initialize debug interface (COM1) */
    152     init_com1();
    153   }
     169  /* initialize libc code */
     170  init_libc();
    154171
    155172  /* print initialization message */
     
    167184
    168185  for (s = cmd_line; *s != 0; s++) {
    169     if (*s == '/' && s[1] != '\0') {
     186    if (*s == '/') {
     187      if ((invert_option = (s[1] == '!')) != 0) {
     188        s++;
     189      }
    170190      s++;
    171       switch(tolower(*s)) {
     191      switch (tolower(*s)) {
     192
     193      case '\0':
     194        /* end of command line; can only happen if command line is incorrect */
     195        cprintf("incomplete command line option\n");
     196        goto init_fail;
    172197
    173198      case 'c':
     
    178203      case 'd':
    179204        /* increase debug level */
    180         debug++;
     205        if (debug++ == 0) {
     206          init_com();
     207        }
    181208        break;
    182209
     
    194221      case 't':
    195222        /* perform thorough PCI scan (i.e. look for individual supported PCI IDs) */
    196         thorough_scan = 1;
     223        thorough_scan = !invert_option;
    197224        break;
    198225
     
    200227        /* reset ports during initialization */
    201228        init_reset = 1;
    202         break;
    203 
    204       case 'i':
    205         /* ignore current adapter index */
    206         if (adapter_index >= 0) {
    207           ad_ignore |= 1U << adapter_index;
    208         }
    209229        break;
    210230
     
    227247        break;
    228248
     249      case 'i':
     250        /* ignore current adapter index */
     251        if (adapter_index >= 0) {
     252          ad_ignore |= 1U << adapter_index;
     253        }
     254        break;
     255
    229256      case 's':
    230         /* set link speed of current port on current adapter */
    231         drv_parm_int(s, link_speed[adapter_index][port_index], u8, 10);
    232         init_reset = 1;
     257        /* enable SCSI emulation for ATAPI devices */
     258        set_port_option(emulate_scsi, !invert_option);
    233259        break;
    234260
    235261      case 'n':
    236262        /* disable NCQ */
    237         if (adapter_index == -1) {
    238           /* disable NCQ on all adapters and ports */
    239           memset(disable_ncq, 0x01, sizeof(disable_ncq));
    240         } else if (port_index == -1) {
    241           /* disable NCQ on all ports of this adapter */
    242           memset(disable_ncq[adapter_index], 0x01, sizeof(*disable_ncq));
    243         } else {
    244           /* disable NCQ for this adapter and port */
    245           disable_ncq[adapter_index][port_index] = 1;
    246         }
     263        set_port_option(disable_ncq, !invert_option);
     264        break;
     265
     266      case 'l':
     267        /* set link speed or power savings */
     268        s++;
     269        switch (tolower(*s)) {
     270        case 's':
     271          /* set link speed */
     272          drv_parm_int(s, optval, int, 10);
     273          set_port_option(link_speed, optval);
     274          break;
     275        case 'p':
     276          /* set power management */
     277          drv_parm_int(s, optval, int, 10);
     278          set_port_option(link_power, optval);
     279          break;
     280        default:
     281          cprintf("invalid link parameter (%c)\n", *s);
     282          goto init_fail;
     283        }
     284        /* need to reset the port in order to establish link settings */
     285        init_reset = 1;
    247286        break;
    248287
     
    402441        iorb->Status = IORB_ERROR;
    403442        iorb->ErrorCode = IOERR_CMD_SYNTAX;
    404         complete_iorb(iorb);
     443        iorb_complete(iorb);
    405444        continue;
    406445      }
     
    744783 * Scan all ports for AHCI devices and construct a DASD device table.
    745784 *
    746  * NOTE: This function may be called multiple times. Only the first invocation
    747  *       will actually scan for devices; all subsequent calls will merely
    748  *       return the results of the initial scan, potentially augmented by
    749  *       modified unit infos after IOCC_CONFIGURATION/IOCM_CHANGE_UNITINFO
    750  *       requests.
     785 * NOTES: This function may be called multiple times. Only the first
     786 *        invocation will actually scan for devices; all subsequent calls will
     787 *        merely return the results of the initial scan, potentially augmented
     788 *        by modified unit infos after IOCC_CONFIGURATION/IOCM_CHANGE_UNITINFO
     789 *        requests.
     790 *
     791 *        In order to support applications that can't deal with ATAPI devies
     792 *        (i.e. need a SCSI adapter) os2ahci will optionally report ATAPI
     793 *        dvices as SCSI devices. The corresponding SCSI adapter doesn't
     794 *        really  exist and is only reported here for the IOCM_GET_DEVICETABLE
     795 *        request. The units attached to this adapter will use the real HW
     796 *        unit IDs, thus we'll never receive a command specific to the
     797 *        emulated SCSI adapter and won't need to set up any sort of entity
     798 *        for it; the only purpose of the emulated SCSI adapter is to pass the
     799 *        bus type "AI_DEVBUS_SCSI_2" upstream, and the emulated units, of
     800 *        course. The emulated SCSI target IDs are allocated as follows:
     801 *
     802 *         0     the virtual adapter
     803 *         1..n  emulated devices; SCSI target ID increments sequentially
    751804 */
    752805void iocm_device_table(IORBH _far *iorb)
     
    755808  DEVICETABLE _far *dt;
    756809  char _far *pos;
     810  int scsi_units = 0;
     811  int scsi_id = 1;
    757812  int rc;
     813  int dta;
    758814  int a;
    759815  int p;
     
    769825  dt->ADDLevelMinor = ADD_LEVEL_MINOR;
    770826  dt->ADDHandle     = add_handle;
    771   dt->TotalAdapters = ad_info_cnt;
    772 
    773   /* Initial position of dynamic portion of device table (i.e. behind the
    774    * array of ADAPTERINFO pointers, pAdapter, in the device table)
    775    */
    776   pos = (char _far *) (dt->pAdapter + ad_info_cnt);
    777 
    778   for (a = 0; a < ad_info_cnt; a++) {
     827  dt->TotalAdapters = ad_info_cnt + 1;
     828   
     829  /* set start of adapter and device information tables */
     830  pos = (char _far *) (dt->pAdapter + dt->TotalAdapters);
     831
     832  /* go through all adapters, including the virtual SCSI adapter */
     833  for (dta = 0; dta < dt->TotalAdapters; dta++) {
    779834    ADAPTERINFO _far *ptr = (ADAPTERINFO _far *) pos;
    780     AD_INFO *ad_info = ad_infos + a;
    781     int units = 0;
    782835
    783836    /* sanity check for sufficient space in device table */
     
    788841    }
    789842
    790     /* set ADAPTERINFO offset in device table */
    791     dt->pAdapter[a] = (ADAPTERINFO _near *) ((u32) ptr & 0xffff);
    792 
    793     /* fill in adapter information structure in device table */
     843    dt->pAdapter[dta] = (ADAPTERINFO _near *) ((u32) ptr & 0xffff);
    794844    memset(ptr, 0x00, sizeof(*ptr));
    795     sprintf(ptr->AdapterName, "AHCI_%d", a);
    796     ptr->AdapterDevBus   = AI_DEVBUS_ST506 | AI_DEVBUS_32BIT;
     845
    797846    ptr->AdapterIOAccess = AI_IOACCESS_BUS_MASTER;
    798847    ptr->AdapterHostBus  = AI_HOSTBUS_OTHER | AI_BUSWIDTH_32BIT;
    799848    ptr->AdapterFlags    = AF_16M | AF_HW_SCATGAT;
    800 
    801     /* AHCI limits S/G elements to 22 bits, thus we'll report only half of
    802      * our S/G list buffers to reduce complexity. The command preparation code
    803      * will always try to map as many S/G elements as possible so the physical
    804      * S/G list capacity is not really wasted except in rare conditions where
    805      * we need to split commands with long S/G lists without any suitable split
    806      * points except those at the reported MaxHWSGList.
    807      */
    808     ptr->MaxHWSGList     = AHCI_MAX_SG / 2;
    809 
    810     if (!ad_info->port_scan_done) {
    811       /* First call; need to scan AHCI hardware for devices. Since this might
    812        * be a lengthy operation, especially when init_reset is set, we'll mark
    813        * the adapter as busy (new IORBs will only be queued but not executed)
    814        * and release the spinlock while scanning the ports so interrupts will
    815        * be processed.
    816        */
    817       if (ad_info->busy) {
    818         dprintf("error: port scan requested while adapter was busy\n");
    819         iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
    820         goto iocm_device_table_done;
     849    ptr->MaxHWSGList     = AHCI_MAX_SG / 2;   /* AHCI S/G elements are 22 bits */
     850
     851    if (dta < ad_info_cnt) {
     852      /* this is a physical AHCI adapter */
     853      AD_INFO *ad_info = ad_infos + dta;
     854
     855      ptr->AdapterDevBus = AI_DEVBUS_ST506 | AI_DEVBUS_32BIT;
     856      sprintf(ptr->AdapterName, "AHCI_%d", dta);
     857
     858      if (!ad_info->port_scan_done) {
     859        /* first call; need to scan AHCI hardware for devices */
     860        if (ad_info->busy) {
     861          dprintf("error: port scan requested while adapter was busy\n");
     862          iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
     863          goto iocm_device_table_done;
     864        }
     865        ad_info->busy = 1;
     866        spin_unlock(drv_lock);
     867        rc = ahci_scan_ports(ad_info);
     868        spin_lock(drv_lock);
     869        ad_info->busy = 0;
     870
     871        if (rc != 0) {
     872          dprintf("error: port scan failed on adapter #%d\n", dta);
     873          iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
     874          goto iocm_device_table_done;
     875        }
     876        ad_info->port_scan_done = 1;
    821877      }
    822       ad_info->busy = 1;
    823       spin_unlock(drv_lock);
    824       rc = ahci_scan_ports(ad_info);
    825       spin_lock(drv_lock);
    826       ad_info->busy = 0;
    827 
    828       if (rc != 0) {
    829         dprintf("error: port scan failed on adapter #%d\n", a);
    830         iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
    831         goto iocm_device_table_done;
    832       }
    833       ad_info->port_scan_done = 1;
    834     }
    835 
    836     /* insert devices (units) into the device table */
    837     for (p = 0; p <= ad_info->port_max; p++) {
    838       for (d = 0; d <= ad_info->ports[p].dev_max; d++) {
    839         if (ad_info->ports[p].devs[d].present) {
    840           UNITINFO _far *ui = ptr->UnitInfo + units;
    841 
    842           /* sanity check for sufficient space in device table */
    843           if ((u32) (ui + 1) - (u32) dt > iorb_conf->DeviceTableLen) {
    844             dprintf("error: device table provided by DASD too small\n");
    845             iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
    846             goto iocm_device_table_done;
     878
     879      /* insert physical (i.e. AHCI) devices into the device table */
     880      for (p = 0; p <= ad_info->port_max; p++) {
     881        for (d = 0; d <= ad_info->ports[p].dev_max; d++) {
     882          if (ad_info->ports[p].devs[d].present) {
     883            if (ad_info->ports[p].devs[d].atapi && emulate_scsi[dta][p]) {
     884              /* only report this unit as SCSI unit */
     885              scsi_units++;
     886              continue;
     887            }
     888            if (add_unit_info(iorb_conf, dta, dta, p, d, 0)) {
     889              goto iocm_device_table_done;
     890            }
    847891          }
    848 
    849           if (ad_info->ports[p].devs[d].unit_info == NULL) {
    850             /* provide initial information about this device (unit) */
    851             memset(ui, 0x00, sizeof(*ui));
    852             ui->AdapterIndex = a;
    853             ui->UnitIndex    = units;
    854             ui->UnitHandle   = iorb_unit(a, p, d);
    855             ui->UnitType     = ad_info->ports[p].devs[d].dev_type;
    856             ui->QueuingCount = ad_info->ports[p].devs[d].ncq_max;;
    857             if (ad_info->ports[p].devs[d].removable) {
    858               ui->UnitFlags |= UF_REMOVABLE;
    859             }
    860           } else {
    861             /* copy updated device (unit) information (IOCM_CHANGE_UNITINFO) */
    862             memcpy(ui, ad_info->ports[p].devs[d].unit_info, sizeof(*ui));
    863           }
    864           units++;
    865892        }
    866893      }
    867     }
    868 
    869     /* set total device (unit) count for this adapter */
    870     ptr->AdapterUnits = units;
     894
     895    } else {
     896      /* this is the virtual SCSI adapter */
     897      if (scsi_units == 0) {
     898        /* not a single unit to be emulated via SCSI */
     899        dt->TotalAdapters--;
     900        break;
     901      }
     902
     903      /* set adapter name and bus type to mimic a SCSI controller */
     904      ptr->AdapterDevBus = AI_DEVBUS_SCSI_2 | AI_DEVBUS_16BIT;
     905      sprintf(ptr->AdapterName, "AHCI_SCSI_0");
     906
     907      /* add all ATAPI units to be emulated by this virtual adaper */
     908      for (a = 0; a < ad_info_cnt; a++) {
     909        AD_INFO *ad_info = ad_infos + a;
     910
     911        for (p = 0; p <= ad_info->port_max; p++) {
     912          for (d = 0; d <= ad_info->ports[p].dev_max; d++) {
     913            if (ad_info->ports[p].devs[d].present &&
     914                ad_info->ports[p].devs[d].atapi &&
     915                emulate_scsi[a][p]) {
     916              if (add_unit_info(iorb_conf, dta, a, p, d, scsi_id++)) {
     917                goto iocm_device_table_done;
     918              }
     919            }
     920          }
     921        }
     922      }
     923    }
    871924
    872925    /* calculate offset for next adapter */
    873     pos = (char _far *) (ptr->UnitInfo + units);
     926    pos = (char _far *) (ptr->UnitInfo + ptr->AdapterUnits);
    874927  }
    875928
     
    10881141 *        Due to this logic, this function is only good for simple task-time
    10891142 *        completions. Functions working on lists of IORBs (such as interrupt
    1090  *        handlers or context hooks) should implement their own logic. See
    1091  *        abort_ctxhook() for an example.
     1143 *        handlers or context hooks) should call iorb_complete() directly and
     1144 *        implement their own logic for removing the IORB from the port queue.
     1145 *        See abort_ctxhook() for an example.
    10921146 */
    10931147void iorb_done(IORBH _far *iorb)
     
    11061160  spin_unlock(drv_lock);
    11071161
    1108   complete_iorb(iorb);
     1162  iorb_complete(iorb);
     1163}
     1164
     1165/******************************************************************************
     1166 * Complete an IORB. It should be called without the adapter-level spinlock
     1167 * to allow the IORB completion routine to perform whatever processing it
     1168 * requires. This implies that the IORB should no longer be in any global
     1169 * queue because the IORB completion routine may well reuse the IORB and send
     1170 * the next request to us before even returning from this function.
     1171 */
     1172void iorb_complete(IORBH _far *iorb)
     1173{
     1174  iorb->Status |= IORB_DONE;
     1175
     1176  dprintf("IORB %Fp complete (status = 0x%04x, error = 0x%04x)\n",
     1177          iorb, iorb->Status, iorb->ErrorCode);
     1178
     1179  if (iorb->RequestControl & IORB_ASYNC_POST) {
     1180    iorb->NotifyAddress(iorb);
     1181  }
    11091182}
    11101183
     
    11261199  memset(aws, 0x00, sizeof(*aws));
    11271200  aws->no_ncq = no_ncq;
     1201}
     1202
     1203/******************************************************************************
     1204 * Free resources in ADD workspace (timer, buffer, ...). This function should
     1205 * be called with the spinlock held to prevent race conditions.
     1206 */
     1207void aws_free(ADD_WORKSPACE _far *aws)
     1208{
     1209  if (aws->timer != 0) {
     1210    ADD_CancelTimer(aws->timer);
     1211    aws->timer = 0;
     1212  }
     1213
     1214  if (aws->buf != NULL) {
     1215    free(aws->buf);
     1216    aws->buf = NULL;
     1217  }
    11281218}
    11291219
     
    12011291{
    12021292}
     1293
     1294/******************************************************************************
     1295 * Add unit info to ADAPTERINFO array (IOCC_GET_DEVICE_TABLE requests). The
     1296 * adapter info array in the device table, dt->pAdapter[], is expected to be
     1297 * initialized for the specified index (dt_ai).
     1298 *
     1299 * Please note that the device table adapter index, dta, is not always equal
     1300 * to the physical adapter index, a: if SCSI emulation has been activated, the
     1301 * last reported adapter is a virtual SCSI adapter and the physical adapter
     1302 * indexes for those units are, of course, different from the device table
     1303 * index of the virtual SCSI adapter.
     1304 */
     1305static int add_unit_info(IORB_CONFIGURATION _far *iorb_conf, int dta,
     1306                         int a, int p, int d, int scsi_id)
     1307{
     1308  DEVICETABLE _far *dt = iorb_conf->pDeviceTable;
     1309  ADAPTERINFO _far *ptr = (ADAPTERINFO _far *) (((u32) dt & 0xffff0000U) +
     1310                                                 (u16) dt->pAdapter[dta]);
     1311  UNITINFO _far *ui = ptr->UnitInfo + ptr->AdapterUnits;
     1312  AD_INFO *ai = ad_infos + a;
     1313
     1314  if ((u32) (ui + 1) - (u32) dt > iorb_conf->DeviceTableLen) {
     1315    dprintf("error: device table provided by DASD too small\n");
     1316    iorb_seterr(&iorb_conf->iorbh, IOERR_CMD_SW_RESOURCE);
     1317    return(-1);
     1318  }
     1319
     1320  if (ai->ports[p].devs[d].unit_info == NULL) {
     1321    /* provide original information about this device (unit) */
     1322    memset(ui, 0x00, sizeof(*ui));
     1323    ui->AdapterIndex = dta;                 /* device table adapter index */
     1324    ui->UnitHandle   = iorb_unit(a, p, d);  /* physical adapter index */
     1325    ui->UnitIndex    = ptr->AdapterUnits;
     1326    ui->UnitType     = ai->ports[p].devs[d].dev_type;
     1327    ui->QueuingCount = ai->ports[p].devs[d].ncq_max;;
     1328    if (ai->ports[p].devs[d].removable) {
     1329      ui->UnitFlags |= UF_REMOVABLE;
     1330    }
     1331    if (scsi_id > 0) {
     1332      /* set fake SCSI ID for this unit */
     1333      ui->UnitSCSITargetID = scsi_id;
     1334    }
     1335  } else {
     1336    /* copy updated device (unit) information (IOCM_CHANGE_UNITINFO) */
     1337    memcpy(ui, ai->ports[p].devs[d].unit_info, sizeof(*ui));
     1338  }
     1339
     1340  ptr->AdapterUnits++;
     1341  return(0);
     1342}
     1343
Note: See TracChangeset for help on using the changeset viewer.