Date: Sun, 5 Apr 2009 18:20:24 +0000 (UTC) From: Andrew Thompson <thompsa@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r190733 - head/sys/dev/usb/storage Message-ID: <200904051820.n35IKOje027868@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: thompsa Date: Sun Apr 5 18:20:24 2009 New Revision: 190733 URL: http://svn.freebsd.org/changeset/base/190733 Log: MFp4 //depot/projects/usb@159908 Reduce the amount of memory used by better sizing buffer arrays. Submitted by: Hans Petter Selasky Modified: head/sys/dev/usb/storage/ustorage_fs.c Modified: head/sys/dev/usb/storage/ustorage_fs.c ============================================================================== --- head/sys/dev/usb/storage/ustorage_fs.c Sun Apr 5 18:20:13 2009 (r190732) +++ head/sys/dev/usb/storage/ustorage_fs.c Sun Apr 5 18:20:24 2009 (r190733) @@ -63,7 +63,15 @@ SYSCTL_INT(_hw_usb2_ustorage_fs, OID_AUT #define USTORAGE_FS_BULK_SIZE (1UL << 17) /* bytes */ #endif -#define USTORAGE_FS_MAX_LUN 8 /* units */ +#ifndef USTORAGE_FS_MAX_LUN +#define USTORAGE_FS_MAX_LUN 8 /* units */ +#endif + +#ifndef USTORAGE_QDATA_MAX +#define USTORAGE_QDATA_MAX 40 /* bytes */ +#endif + +#define sc_cmd_data sc_cbw.CBWCDB /* * The SCSI ID string must be exactly 28 characters long @@ -172,7 +180,6 @@ struct ustorage_fs_softc { uint8_t cbw_dir; uint8_t cmd_dir; uint8_t lun; - uint8_t cmd_data[CBWCDBLENGTH]; uint8_t cmd_len; uint8_t data_short:1; uint8_t data_error:1; @@ -182,13 +189,10 @@ struct ustorage_fs_softc { struct usb2_device *sc_udev; struct usb2_xfer *sc_xfer[USTORAGE_FS_T_BBB_MAX]; - uint32_t sc_unit; - - uint8_t sc_name[16]; uint8_t sc_iface_no; /* interface number */ uint8_t sc_last_lun; uint8_t sc_last_xfer_index; - uint8_t sc_qdata[1024]; + uint8_t sc_qdata[USTORAGE_QDATA_MAX]; }; /* prototypes */ @@ -258,45 +262,50 @@ struct usb2_config ustorage_fs_bbb_confi .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, - .md.bufsize = sizeof(ustorage_fs_bbb_cbw_t), - .md.flags = {.ext_buffer = 1,}, - .md.callback = &ustorage_fs_t_bbb_command_callback, + .bufsize = sizeof(ustorage_fs_bbb_cbw_t), + .flags = {.ext_buffer = 1,}, + .callback = &ustorage_fs_t_bbb_command_callback, + .usb_mode = USB_MODE_DEVICE, }, [USTORAGE_FS_T_BBB_DATA_DUMP] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, - .md.bufsize = 0, /* use wMaxPacketSize */ - .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, - .md.callback = &ustorage_fs_t_bbb_data_dump_callback, + .bufsize = 0, /* use wMaxPacketSize */ + .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, + .callback = &ustorage_fs_t_bbb_data_dump_callback, + .usb_mode = USB_MODE_DEVICE, }, [USTORAGE_FS_T_BBB_DATA_READ] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, - .md.bufsize = USTORAGE_FS_BULK_SIZE, - .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer = 1}, - .md.callback = &ustorage_fs_t_bbb_data_read_callback, + .bufsize = USTORAGE_FS_BULK_SIZE, + .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer = 1}, + .callback = &ustorage_fs_t_bbb_data_read_callback, + .usb_mode = USB_MODE_DEVICE, }, [USTORAGE_FS_T_BBB_DATA_WRITE] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, - .md.bufsize = USTORAGE_FS_BULK_SIZE, - .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer = 1}, - .md.callback = &ustorage_fs_t_bbb_data_write_callback, + .bufsize = USTORAGE_FS_BULK_SIZE, + .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer = 1}, + .callback = &ustorage_fs_t_bbb_data_write_callback, + .usb_mode = USB_MODE_DEVICE, }, [USTORAGE_FS_T_BBB_STATUS] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, - .md.bufsize = sizeof(ustorage_fs_bbb_csw_t), - .md.flags = {.short_xfer_ok = 1,.ext_buffer = 1,}, - .md.callback = &ustorage_fs_t_bbb_status_callback, + .bufsize = sizeof(ustorage_fs_bbb_csw_t), + .flags = {.short_xfer_ok = 1,.ext_buffer = 1,}, + .callback = &ustorage_fs_t_bbb_status_callback, + .usb_mode = USB_MODE_DEVICE, }, }; @@ -335,6 +344,7 @@ ustorage_fs_attach(device_t dev) struct usb2_attach_arg *uaa = device_get_ivars(dev); struct usb2_interface_descriptor *id; int err; + int unit; /* * NOTE: the softc struct is bzero-ed in device_set_driver. @@ -344,9 +354,9 @@ ustorage_fs_attach(device_t dev) sc->sc_dev = dev; sc->sc_udev = uaa->device; - sc->sc_unit = device_get_unit(dev); + unit = device_get_unit(dev); - if (sc->sc_unit == 0) { + if (unit == 0) { if (ustorage_fs_ramdisk == NULL) { /* * allocate a memory image for our ramdisk until @@ -362,8 +372,6 @@ ustorage_fs_attach(device_t dev) sc->sc_lun[0].num_sectors = USTORAGE_FS_RAM_SECT; sc->sc_lun[0].removable = 1; } - snprintf(sc->sc_name, sizeof(sc->sc_name), - "%s", device_get_nameunit(dev)); device_set_usb2_desc(dev); @@ -544,11 +552,6 @@ ustorage_fs_t_bbb_command_callback(struc sc->sc_transfer.cmd_len); break; } - bcopy(sc->sc_cbw.CBWCDB, sc->sc_transfer.cmd_data, - sc->sc_transfer.cmd_len); - - bzero(sc->sc_cbw.CBWCDB + sc->sc_transfer.cmd_len, - sizeof(sc->sc_cbw.CBWCDB) - sc->sc_transfer.cmd_len); error = ustorage_fs_do_cmd(sc); if (error) { @@ -927,17 +930,17 @@ ustorage_fs_verify(struct ustorage_fs_so /* * Get the starting Logical Block Address */ - lba = get_be32(&sc->sc_transfer.cmd_data[2]); + lba = get_be32(&sc->sc_cmd_data[2]); /* * We allow DPO (Disable Page Out = don't save data in the cache) * but we don't implement it. */ - if ((sc->sc_transfer.cmd_data[1] & ~0x10) != 0) { + if ((sc->sc_cmd_data[1] & ~0x10) != 0) { currlun->sense_data = SS_INVALID_FIELD_IN_CDB; return (1); } - vlen = get_be16(&sc->sc_transfer.cmd_data[7]); + vlen = get_be16(&sc->sc_cmd_data[7]); if (vlen == 0) { goto done; } @@ -998,6 +1001,9 @@ ustorage_fs_inquiry(struct ustorage_fs_s /* Copy in ID string */ memcpy(buf + 8, USTORAGE_FS_ID_STRING, 28); +#if (USTORAGE_QDATA_MAX < 36) +#error "(USTORAGE_QDATA_MAX < 36)" +#endif return (ustorage_fs_min_len(sc, 36, 0 - 1)); } @@ -1063,10 +1069,13 @@ ustorage_fs_request_sense(struct ustorag /* Additional sense length */ buf[12] = ASC(sd); buf[13] = ASCQ(sd); + +#if (USTORAGE_QDATA_MAX < 18) +#error "(USTORAGE_QDATA_MAX < 18)" +#endif return (ustorage_fs_min_len(sc, 18, 0 - 1)); } - /*------------------------------------------------------------------------* * ustorage_fs_read_capacity * @@ -1079,22 +1088,25 @@ ustorage_fs_read_capacity(struct ustorag { uint8_t *buf = sc->sc_transfer.data_ptr; struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; - uint32_t lba = get_be32(&sc->sc_transfer.cmd_data[2]); - uint8_t pmi = sc->sc_transfer.cmd_data[8]; + uint32_t lba = get_be32(&sc->sc_cmd_data[2]); + uint8_t pmi = sc->sc_cmd_data[8]; /* Check the PMI and LBA fields */ if ((pmi > 1) || ((pmi == 0) && (lba != 0))) { currlun->sense_data = SS_INVALID_FIELD_IN_CDB; return (1); } - put_be32(&buf[0], currlun->num_sectors - 1); /* Max logical block */ - put_be32(&buf[4], 512); + put_be32(&buf[0], currlun->num_sectors - 1); /* Block length */ + put_be32(&buf[4], 512); + +#if (USTORAGE_QDATA_MAX < 8) +#error "(USTORAGE_QDATA_MAX < 8)" +#endif return (ustorage_fs_min_len(sc, 8, 0 - 1)); } - /*------------------------------------------------------------------------* * ustorage_fs_mode_sense * @@ -1110,7 +1122,7 @@ ustorage_fs_mode_sense(struct ustorage_f uint8_t *buf0; uint16_t len; uint16_t limit; - uint8_t mscmnd = sc->sc_transfer.cmd_data[0]; + uint8_t mscmnd = sc->sc_cmd_data[0]; uint8_t pc; uint8_t page_code; uint8_t changeable_values; @@ -1118,13 +1130,13 @@ ustorage_fs_mode_sense(struct ustorage_f buf0 = buf; - if ((sc->sc_transfer.cmd_data[1] & ~0x08) != 0) { + if ((sc->sc_cmd_data[1] & ~0x08) != 0) { /* Mask away DBD */ currlun->sense_data = SS_INVALID_FIELD_IN_CDB; return (1); } - pc = sc->sc_transfer.cmd_data[2] >> 6; - page_code = sc->sc_transfer.cmd_data[2] & 0x3f; + pc = sc->sc_cmd_data[2] >> 6; + page_code = sc->sc_cmd_data[2] & 0x3f; if (pc == 3) { currlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; return (1); @@ -1195,6 +1207,10 @@ ustorage_fs_mode_sense(struct ustorage_f buf0[0] = len - 1; else put_be16(buf0, len - 2); + +#if (USTORAGE_QDATA_MAX < 24) +#error "(USTORAGE_QDATA_MAX < 24)" +#endif return (ustorage_fs_min_len(sc, len, 0 - 1)); } @@ -1217,9 +1233,9 @@ ustorage_fs_start_stop(struct ustorage_f currlun->sense_data = SS_INVALID_COMMAND; return (1); } - immed = sc->sc_transfer.cmd_data[1] & 0x01; - loej = sc->sc_transfer.cmd_data[4] & 0x02; - start = sc->sc_transfer.cmd_data[4] & 0x01; + immed = sc->sc_cmd_data[1] & 0x01; + loej = sc->sc_cmd_data[4] & 0x02; + start = sc->sc_cmd_data[4] & 0x01; if (immed || loej || start) { /* compile fix */ @@ -1244,8 +1260,8 @@ ustorage_fs_prevent_allow(struct ustorag currlun->sense_data = SS_INVALID_COMMAND; return (1); } - prevent = sc->sc_transfer.cmd_data[4] & 0x01; - if ((sc->sc_transfer.cmd_data[4] & ~0x01) != 0) { + prevent = sc->sc_cmd_data[4] & 0x01; + if ((sc->sc_cmd_data[4] & ~0x01) != 0) { /* Mask away Prevent */ currlun->sense_data = SS_INVALID_FIELD_IN_CDB; return (1); @@ -1275,12 +1291,16 @@ ustorage_fs_read_format_capacities(struc /* Only the Current / Maximum Capacity Descriptor */ buf += 4; - put_be32(&buf[0], currlun->num_sectors); /* Number of blocks */ - put_be32(&buf[4], 512); + put_be32(&buf[0], currlun->num_sectors); /* Block length */ - buf[4] = 0x02; + put_be32(&buf[4], 512); /* Current capacity */ + buf[4] = 0x02; + +#if (USTORAGE_QDATA_MAX < 12) +#error "(USTORAGE_QDATA_MAX < 12)" +#endif return (ustorage_fs_min_len(sc, 12, 0 - 1)); } @@ -1345,18 +1365,18 @@ ustorage_fs_read(struct ustorage_fs_soft * Get the starting Logical Block Address and check that it's not * too big */ - if (sc->sc_transfer.cmd_data[0] == SC_READ_6) { - lba = (((uint32_t)sc->sc_transfer.cmd_data[1]) << 16) | - get_be16(&sc->sc_transfer.cmd_data[2]); + if (sc->sc_cmd_data[0] == SC_READ_6) { + lba = (((uint32_t)sc->sc_cmd_data[1]) << 16) | + get_be16(&sc->sc_cmd_data[2]); } else { - lba = get_be32(&sc->sc_transfer.cmd_data[2]); + lba = get_be32(&sc->sc_cmd_data[2]); /* * We allow DPO (Disable Page Out = don't save data in the * cache) and FUA (Force Unit Access = don't read from the * cache), but we don't implement them. */ - if ((sc->sc_transfer.cmd_data[1] & ~0x18) != 0) { + if ((sc->sc_cmd_data[1] & ~0x18) != 0) { currlun->sense_data = SS_INVALID_FIELD_IN_CDB; return (1); } @@ -1403,11 +1423,11 @@ ustorage_fs_write(struct ustorage_fs_sof * Get the starting Logical Block Address and check that it's not * too big. */ - if (sc->sc_transfer.cmd_data[0] == SC_WRITE_6) - lba = (((uint32_t)sc->sc_transfer.cmd_data[1]) << 16) | - get_be16(&sc->sc_transfer.cmd_data[2]); + if (sc->sc_cmd_data[0] == SC_WRITE_6) + lba = (((uint32_t)sc->sc_cmd_data[1]) << 16) | + get_be16(&sc->sc_cmd_data[2]); else { - lba = get_be32(&sc->sc_transfer.cmd_data[2]); + lba = get_be32(&sc->sc_cmd_data[2]); /* * We allow DPO (Disable Page Out = don't save data in the @@ -1415,11 +1435,11 @@ ustorage_fs_write(struct ustorage_fs_sof * medium). We don't implement DPO; we implement FUA by * performing synchronous output. */ - if ((sc->sc_transfer.cmd_data[1] & ~0x18) != 0) { + if ((sc->sc_cmd_data[1] & ~0x18) != 0) { currlun->sense_data = SS_INVALID_FIELD_IN_CDB; return (1); } - if (sc->sc_transfer.cmd_data[1] & 0x08) { + if (sc->sc_cmd_data[1] & 0x08) { /* FUA */ /* XXX set SYNC flag here */ } @@ -1495,7 +1515,7 @@ ustorage_fs_check_cmd(struct ustorage_fs uint16_t mask, uint8_t needs_medium) { struct ustorage_fs_lun *currlun; - uint8_t lun = (sc->sc_transfer.cmd_data[1] >> 5); + uint8_t lun = (sc->sc_cmd_data[1] >> 5); uint8_t i; /* Verify the length of the command itself */ @@ -1506,7 +1526,7 @@ ustorage_fs_check_cmd(struct ustorage_fs return (1); } /* Mask away the LUN */ - sc->sc_transfer.cmd_data[1] &= 0x1f; + sc->sc_cmd_data[1] &= 0x1f; /* Check if LUN is correct */ if (lun != sc->sc_transfer.lun) { @@ -1516,7 +1536,7 @@ ustorage_fs_check_cmd(struct ustorage_fs if (sc->sc_transfer.lun <= sc->sc_last_lun) { sc->sc_transfer.currlun = currlun = sc->sc_lun + sc->sc_transfer.lun; - if (sc->sc_transfer.cmd_data[0] != SC_REQUEST_SENSE) { + if (sc->sc_cmd_data[0] != SC_REQUEST_SENSE) { currlun->sense_data = SS_NO_SENSE; currlun->sense_data_info = 0; currlun->info_valid = 0; @@ -1527,8 +1547,8 @@ ustorage_fs_check_cmd(struct ustorage_fs * else must fail! */ if ((currlun->unit_attention_data != SS_NO_SENSE) && - (sc->sc_transfer.cmd_data[0] != SC_INQUIRY) && - (sc->sc_transfer.cmd_data[0] != SC_REQUEST_SENSE)) { + (sc->sc_cmd_data[0] != SC_INQUIRY) && + (sc->sc_cmd_data[0] != SC_REQUEST_SENSE)) { currlun->sense_data = currlun->unit_attention_data; currlun->unit_attention_data = SS_NO_SENSE; return (1); @@ -1540,8 +1560,8 @@ ustorage_fs_check_cmd(struct ustorage_fs * INQUIRY and REQUEST SENSE commands are explicitly allowed * to use unsupported LUNs; all others may not. */ - if ((sc->sc_transfer.cmd_data[0] != SC_INQUIRY) && - (sc->sc_transfer.cmd_data[0] != SC_REQUEST_SENSE)) { + if ((sc->sc_cmd_data[0] != SC_INQUIRY) && + (sc->sc_cmd_data[0] != SC_REQUEST_SENSE)) { return (1); } } @@ -1551,7 +1571,7 @@ ustorage_fs_check_cmd(struct ustorage_fs * non-zero. */ for (i = 0; i != min_cmd_size; i++) { - if (sc->sc_transfer.cmd_data[i] && !(mask & (1UL << i))) { + if (sc->sc_cmd_data[i] && !(mask & (1UL << i))) { if (currlun) { currlun->sense_data = SS_INVALID_FIELD_IN_CDB; } @@ -1589,12 +1609,12 @@ ustorage_fs_do_cmd(struct ustorage_fs_so sc->sc_transfer.data_ptr = sc->sc_qdata; DPRINTF("cmd_data[0]=0x%02x, data_rem=0x%08x\n", - sc->sc_transfer.cmd_data[0], sc->sc_transfer.data_rem); + sc->sc_cmd_data[0], sc->sc_transfer.data_rem); - switch (sc->sc_transfer.cmd_data[0]) { + switch (sc->sc_cmd_data[0]) { case SC_INQUIRY: sc->sc_transfer.cmd_dir = DIR_WRITE; - error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1); + error = ustorage_fs_min_len(sc, sc->sc_cmd_data[4], 0 - 1); if (error) { break; } @@ -1609,7 +1629,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so case SC_MODE_SELECT_6: sc->sc_transfer.cmd_dir = DIR_READ; - error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1); + error = ustorage_fs_min_len(sc, sc->sc_cmd_data[4], 0 - 1); if (error) { break; } @@ -1625,7 +1645,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so case SC_MODE_SELECT_10: sc->sc_transfer.cmd_dir = DIR_READ; error = ustorage_fs_min_len(sc, - get_be16(&sc->sc_transfer.cmd_data[7]), 0 - 1); + get_be16(&sc->sc_cmd_data[7]), 0 - 1); if (error) { break; } @@ -1640,7 +1660,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so case SC_MODE_SENSE_6: sc->sc_transfer.cmd_dir = DIR_WRITE; - error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1); + error = ustorage_fs_min_len(sc, sc->sc_cmd_data[4], 0 - 1); if (error) { break; } @@ -1656,7 +1676,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so case SC_MODE_SENSE_10: sc->sc_transfer.cmd_dir = DIR_WRITE; error = ustorage_fs_min_len(sc, - get_be16(&sc->sc_transfer.cmd_data[7]), 0 - 1); + get_be16(&sc->sc_cmd_data[7]), 0 - 1); if (error) { break; } @@ -1684,7 +1704,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so break; case SC_READ_6: - i = sc->sc_transfer.cmd_data[4]; + i = sc->sc_cmd_data[4]; sc->sc_transfer.cmd_dir = DIR_WRITE; temp = ((i == 0) ? 256UL : i); error = ustorage_fs_min_len(sc, temp << 9, mask9); @@ -1702,7 +1722,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so case SC_READ_10: sc->sc_transfer.cmd_dir = DIR_WRITE; - temp = get_be16(&sc->sc_transfer.cmd_data[7]); + temp = get_be16(&sc->sc_cmd_data[7]); error = ustorage_fs_min_len(sc, temp << 9, mask9); if (error) { break; @@ -1718,7 +1738,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so case SC_READ_12: sc->sc_transfer.cmd_dir = DIR_WRITE; - temp = get_be32(&sc->sc_transfer.cmd_data[6]); + temp = get_be32(&sc->sc_cmd_data[6]); if (temp >= (1UL << (32 - 9))) { /* numerical overflow */ sc->sc_csw.bCSWStatus = CSWSTATUS_FAILED; @@ -1752,7 +1772,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so case SC_READ_FORMAT_CAPACITIES: sc->sc_transfer.cmd_dir = DIR_WRITE; error = ustorage_fs_min_len(sc, - get_be16(&sc->sc_transfer.cmd_data[7]), 0 - 1); + get_be16(&sc->sc_cmd_data[7]), 0 - 1); if (error) { break; } @@ -1767,7 +1787,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so case SC_REQUEST_SENSE: sc->sc_transfer.cmd_dir = DIR_WRITE; - error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1); + error = ustorage_fs_min_len(sc, sc->sc_cmd_data[4], 0 - 1); if (error) { break; } @@ -1836,7 +1856,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so break; case SC_WRITE_6: - i = sc->sc_transfer.cmd_data[4]; + i = sc->sc_cmd_data[4]; sc->sc_transfer.cmd_dir = DIR_READ; temp = ((i == 0) ? 256UL : i); error = ustorage_fs_min_len(sc, temp << 9, mask9); @@ -1854,7 +1874,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so case SC_WRITE_10: sc->sc_transfer.cmd_dir = DIR_READ; - temp = get_be16(&sc->sc_transfer.cmd_data[7]); + temp = get_be16(&sc->sc_cmd_data[7]); error = ustorage_fs_min_len(sc, temp << 9, mask9); if (error) { break; @@ -1870,7 +1890,7 @@ ustorage_fs_do_cmd(struct ustorage_fs_so case SC_WRITE_12: sc->sc_transfer.cmd_dir = DIR_READ; - temp = get_be32(&sc->sc_transfer.cmd_data[6]); + temp = get_be32(&sc->sc_cmd_data[6]); if (temp > (mask9 >> 9)) { /* numerical overflow */ sc->sc_csw.bCSWStatus = CSWSTATUS_FAILED;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200904051820.n35IKOje027868>