7f4d10be2c
and fixing some major bugs. - Add support for the v5 firmware interface, used by the DAC1164P (tested) and AcceleRAID 352 (untested but should work). We now cover all of the Mylex family's protocols except for v2 (used by EISA and Alpha-compatible cards). - Fix an accounting bug which resulted in endless 'poll still busy' messages. In situations of high controller load the count of poll commands could be incremented without actually successfully launching a command. This totally removes the accounting for status poll commnads; it was its own worst enemy. - Add some simple reentry prevention locks to processing of the waiting and completed command queues to prevent races which could result in I/O being done or completed twice (both are fatal). This highlights a need for simple locking primitives in both the UP and SMP kernels. - Streamline the handling of command completion to reduce the amount of redundant work being done. Remove the code which tests for commands that have gone missing in action; nobody has ever seen one of these and it wouldn't have worked properly anyhow. - Handle disconnection of drives from the controller in the detach, not shutdown method. This avoids problems flushing the cache in a panic when a drive is mounted. - Don't call bus_generic_detach when disconnecting drives; it doesn't actually do anything useful. - Increment the log message index regardless of whether we actually retrieved one or not. If we run into a message that we can't fetch, we don't want to spin endlessly complaining about the fact. - Don't assume that interrupts will work when we're flushing the controller. We may think they are enabled, but in eg. a panic situation the controller may not be able to deliver an interrupt.
409 lines
14 KiB
C
409 lines
14 KiB
C
/*-
|
|
* Copyright (c) 1999 Michael Smith
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#define MLX_CFG_BASE0 0x10 /* first region */
|
|
#define MLX_CFG_BASE1 0x14 /* second region (type 3 only) */
|
|
|
|
#define MLX_BLKSIZE 512 /* fixed feature */
|
|
|
|
/*
|
|
* Selected command codes.
|
|
*/
|
|
#define MLX_CMD_ENQUIRY 0x53
|
|
#define MLX_CMD_ENQUIRY2 0x1c
|
|
#define MLX_CMD_ENQSYSDRIVE 0x19
|
|
#define MLX_CMD_READOLDSG 0xb6
|
|
#define MLX_CMD_WRITEOLDSG 0xb7
|
|
#define MLX_CMD_FLUSH 0x0a
|
|
#define MLX_CMD_LOGOP 0x72
|
|
#define MLX_CMD_REBUILDASYNC 0x16
|
|
#define MLX_CMD_CHECKASYNC 0x1e
|
|
#define MLX_CMD_REBUILDSTAT 0x0c
|
|
#define MLX_CMD_STOPCHANNEL 0x13
|
|
#define MLX_CMD_STARTCHANNEL 0x12
|
|
|
|
/*
|
|
* Status values.
|
|
*/
|
|
#define MLX_STATUS_OK 0x0000
|
|
#define MLX_STATUS_RDWROFFLINE 0x0002 /* read/write claims drive is offline */
|
|
#define MLX_STATUS_WEDGED 0xdead /* controller not listening */
|
|
#define MLX_STATUS_LOST 0xbeef /* never came back */
|
|
#define MLX_STATUS_BUSY 0xffff /* command is in controller */
|
|
|
|
/*
|
|
* Accessor defines for the V3 interface.
|
|
*/
|
|
#define MLX_V3_MAILBOX 0x00
|
|
#define MLX_V3_STATUS_IDENT 0x0d
|
|
#define MLX_V3_STATUS 0x0e
|
|
#define MLX_V3_IDBR 0x40
|
|
#define MLX_V3_ODBR 0x41
|
|
#define MLX_V3_IER 0x43
|
|
|
|
#define MLX_V3_PUT_MAILBOX(sc, idx, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_MAILBOX + idx, val)
|
|
#define MLX_V3_GET_STATUS_IDENT(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_STATUS_IDENT)
|
|
#define MLX_V3_GET_STATUS(sc) bus_space_read_2 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_STATUS)
|
|
#define MLX_V3_GET_IDBR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_IDBR)
|
|
#define MLX_V3_PUT_IDBR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_IDBR, val)
|
|
#define MLX_V3_GET_ODBR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_ODBR)
|
|
#define MLX_V3_PUT_ODBR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_ODBR, val)
|
|
#define MLX_V3_PUT_IER(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_IER, val)
|
|
|
|
#define MLX_V3_IDB_FULL (1<<0) /* mailbox is full */
|
|
#define MLX_V3_IDB_SACK (1<<1) /* acknowledge status read */
|
|
|
|
#define MLX_V3_ODB_SAVAIL (1<<0) /* status is available */
|
|
|
|
/*
|
|
* Accessor defines for the V4 interface.
|
|
*/
|
|
#define MLX_V4_MAILBOX 0x1000
|
|
#define MLX_V4_STATUS_IDENT 0x1018
|
|
#define MLX_V4_STATUS 0x101a
|
|
#define MLX_V4_IDBR 0x0020
|
|
#define MLX_V4_ODBR 0x002c
|
|
#define MLX_V4_IER 0x0034
|
|
|
|
/* use longword access? */
|
|
#define MLX_V4_PUT_MAILBOX(sc, idx, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX + idx, val)
|
|
#define MLX_V4_GET_STATUS_IDENT(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_STATUS_IDENT)
|
|
#define MLX_V4_GET_STATUS(sc) bus_space_read_2 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_STATUS)
|
|
#define MLX_V4_GET_IDBR(sc) bus_space_read_4 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_IDBR)
|
|
#define MLX_V4_PUT_IDBR(sc, val) bus_space_write_4(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_IDBR, val)
|
|
#define MLX_V4_GET_ODBR(sc) bus_space_read_4 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_ODBR)
|
|
#define MLX_V4_PUT_ODBR(sc, val) bus_space_write_4(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_ODBR, val)
|
|
#define MLX_V4_PUT_IER(sc, val) bus_space_write_4(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_IER, val)
|
|
|
|
#define MLX_V4_IDB_FULL (1<<0) /* mailbox is full */
|
|
|
|
#define MLX_V4_IDB_HWMBOX_CMD (1<<0) /* posted hardware mailbox command */
|
|
#define MLX_V4_IDB_SACK (1<<1) /* acknowledge status read */
|
|
#define MLX_V4_IDB_MEMMBOX_CMD (1<<4) /* posted memory mailbox command */
|
|
|
|
#define MLX_V4_ODB_HWSAVAIL (1<<0) /* status is available for hardware mailbox */
|
|
#define MLX_V4_ODB_MEMSAVAIL (1<<1) /* status is available for memory mailbox */
|
|
|
|
#define MLX_V4_ODB_HWMBOX_ACK (1<<0) /* ack status read from hardware mailbox */
|
|
#define MLX_V4_ODB_MEMMBOX_ACK (1<<1) /* ack status read from memory mailbox */
|
|
|
|
#define MLX_V4_IER_MASK 0xfb /* message unit interrupt mask */
|
|
#define MLX_V4_IER_DISINT (1<<2) /* interrupt disable bit */
|
|
|
|
/*
|
|
* Accessor defines for the V5 interface
|
|
*/
|
|
#define MLX_V5_MAILBOX 0x50
|
|
#define MLX_V5_STATUS_IDENT 0x5d
|
|
#define MLX_V5_STATUS 0x5e
|
|
#define MLX_V5_IDBR 0x60
|
|
#define MLX_V5_ODBR 0x61
|
|
#define MLX_V5_IER 0x34
|
|
|
|
#define MLX_V5_PUT_MAILBOX(sc, idx, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_MAILBOX + idx, val)
|
|
#define MLX_V5_GET_STATUS_IDENT(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_STATUS_IDENT)
|
|
#define MLX_V5_GET_STATUS(sc) bus_space_read_2 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_STATUS)
|
|
#define MLX_V5_GET_IDBR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_IDBR)
|
|
#define MLX_V5_PUT_IDBR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_IDBR, val)
|
|
#define MLX_V5_GET_ODBR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_ODBR)
|
|
#define MLX_V5_PUT_ODBR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_ODBR, val)
|
|
#define MLX_V5_PUT_IER(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_IER, val)
|
|
|
|
#define MLX_V5_IDB_EMPTY (1<<0) /* mailbox is empty */
|
|
|
|
#define MLX_V5_IDB_HWMBOX_CMD (1<<0) /* posted hardware mailbox command */
|
|
#define MLX_V5_IDB_SACK (1<<1) /* acknowledge status read */
|
|
#define MLX_V5_IDB_MEMMBOX_CMD (1<<4) /* posted memory mailbox command */
|
|
|
|
#define MLX_V5_ODB_HWSAVAIL (1<<0) /* status is available for hardware mailbox */
|
|
#define MLX_V5_ODB_MEMSAVAIL (1<<1) /* status is available for memory mailbox */
|
|
|
|
#define MLX_V5_ODB_HWMBOX_ACK (1<<0) /* ack status read from hardware mailbox */
|
|
#define MLX_V5_ODB_MEMMBOX_ACK (1<<1) /* ack status read from memory mailbox */
|
|
|
|
#define MLX_V5_IER_DISINT (1<<2) /* interrupt disable bit */
|
|
|
|
|
|
/*
|
|
* Scatter-gather list format, type 1, kind 00.
|
|
*/
|
|
struct mlx_sgentry
|
|
{
|
|
u_int32_t sg_addr;
|
|
u_int32_t sg_count;
|
|
} __attribute__ ((packed));
|
|
|
|
/*
|
|
* Command result buffers, as placed in system memory by the controller.
|
|
*/
|
|
struct mlx_enquiry /* MLX_CMD_ENQUIRY */
|
|
{
|
|
u_int8_t me_num_sys_drvs;
|
|
u_int8_t res1[3];
|
|
u_int32_t me_drvsize[32];
|
|
u_int16_t me_flash_age;
|
|
u_int8_t me_status_flags;
|
|
#define MLX_ENQ_SFLAG_DEFWRERR (1<<0) /* deferred write error indicator */
|
|
#define MLX_ENQ_SFLAG_BATTLOW (1<<1) /* battery low */
|
|
u_int8_t res2;
|
|
u_int8_t me_fwminor;
|
|
u_int8_t me_fwmajor;
|
|
u_int8_t me_rebuild_flag;
|
|
u_int8_t me_max_commands;
|
|
u_int8_t me_offline_sd_count;
|
|
u_int8_t res3;
|
|
u_int16_t me_event_log_seq_num;
|
|
u_int8_t me_critical_sd_count;
|
|
u_int8_t res4[3];
|
|
u_int8_t me_dead_count;
|
|
u_int8_t res5;
|
|
u_int8_t me_rebuild_count;
|
|
u_int8_t me_misc_flags;
|
|
#define MLX_ENQ_MISC_BBU (1<<3) /* battery backup present */
|
|
struct
|
|
{
|
|
u_int8_t dd_targ;
|
|
u_int8_t dd_chan;
|
|
} __attribute__ ((packed)) me_dead[20];
|
|
} __attribute__ ((packed));
|
|
|
|
struct mlx_enquiry2 /* MLX_CMD_ENQUIRY2 */
|
|
{
|
|
u_int32_t me_hardware_id;
|
|
u_int32_t me_firmware_id;
|
|
u_int32_t res1;
|
|
u_int8_t me_configured_channels;
|
|
u_int8_t me_actual_channels;
|
|
u_int8_t me_max_targets;
|
|
u_int8_t me_max_tags;
|
|
u_int8_t me_max_sys_drives;
|
|
u_int8_t me_max_arms;
|
|
u_int8_t me_max_spans;
|
|
u_int8_t res2;
|
|
u_int32_t res3;
|
|
u_int32_t me_mem_size;
|
|
u_int32_t me_cache_size;
|
|
u_int32_t me_flash_size;
|
|
u_int32_t me_nvram_size;
|
|
u_int16_t me_mem_type;
|
|
u_int16_t me_clock_speed;
|
|
u_int16_t me_mem_speed;
|
|
u_int16_t me_hardware_speed;
|
|
u_int8_t res4[10];
|
|
u_int16_t me_max_commands;
|
|
u_int16_t me_max_sg;
|
|
u_int16_t me_max_dp;
|
|
u_int16_t me_max_iod;
|
|
u_int16_t me_max_comb;
|
|
u_int8_t me_latency;
|
|
u_int8_t res5;
|
|
u_int8_t me_scsi_timeout;
|
|
u_int8_t res6;
|
|
u_int16_t me_min_freelines;
|
|
u_int8_t res7[8];
|
|
u_int8_t me_rate_const;
|
|
u_int8_t res8[11];
|
|
u_int16_t me_physblk;
|
|
u_int16_t me_logblk;
|
|
u_int16_t me_maxblk;
|
|
u_int16_t me_blocking_factor;
|
|
u_int16_t me_cacheline;
|
|
u_int8_t me_scsi_cap;
|
|
u_int8_t res9[5];
|
|
u_int16_t me_fimware_build;
|
|
u_int8_t me_fault_mgmt_type;
|
|
u_int8_t res10;
|
|
u_int32_t me_firmware_features;
|
|
u_int8_t res11[8];
|
|
} __attribute__ ((packed));
|
|
|
|
struct mlx_enq_sys_drive /* MLX_CMD_ENQSYSDRIVE returns an array of 32 of these */
|
|
{
|
|
u_int32_t sd_size;
|
|
u_int8_t sd_state;
|
|
u_int8_t sd_raidlevel;
|
|
u_int16_t res1;
|
|
} __attribute__ ((packed));
|
|
|
|
struct mlx_eventlog_entry /* MLX_CMD_LOGOP/MLX_LOGOP_GET */
|
|
{
|
|
u_int8_t el_type;
|
|
u_int8_t el_length;
|
|
u_char el_target:5;
|
|
u_char el_channel:3;
|
|
u_char el_lun:6;
|
|
u_char res1:2;
|
|
u_int16_t el_seqno;
|
|
u_char el_errorcode:7;
|
|
u_char el_valid:1;
|
|
u_int8_t el_segment;
|
|
u_char el_sensekey:4;
|
|
u_char res2:1;
|
|
u_char el_ILI:1;
|
|
u_char el_EOM:1;
|
|
u_char el_filemark:1;
|
|
u_int8_t el_information[4];
|
|
u_int8_t el_addsense;
|
|
u_int8_t el_csi[4];
|
|
u_int8_t el_asc;
|
|
u_int8_t el_asq;
|
|
u_int8_t res3[12];
|
|
} __attribute__ ((packed));
|
|
|
|
#define MLX_LOGOP_GET 0x00 /* operation codes for MLX_CMD_LOGOP */
|
|
#define MLX_LOGMSG_SENSE 0x00 /* log message contents codes */
|
|
|
|
struct mlx_rebuild_stat /* MLX_CMD_REBUILDSTAT */
|
|
{
|
|
u_int32_t rb_drive;
|
|
u_int32_t rb_size;
|
|
u_int32_t rb_remaining;
|
|
} __attribute__ ((packed));
|
|
|
|
/*
|
|
* Inlines to build various command structures
|
|
*/
|
|
static __inline void
|
|
mlx_make_type1(struct mlx_command *mc,
|
|
u_int8_t code,
|
|
u_int16_t f1,
|
|
u_int32_t f2,
|
|
u_int8_t f3,
|
|
u_int32_t f4,
|
|
u_int8_t f5)
|
|
{
|
|
mc->mc_mailbox[0x0] = code;
|
|
mc->mc_mailbox[0x2] = f1 & 0xff;
|
|
mc->mc_mailbox[0x3] = (((f2 >> 24) & 0x3) << 6) | ((f1 >> 8) & 0x3f);
|
|
mc->mc_mailbox[0x4] = f2 & 0xff;
|
|
mc->mc_mailbox[0x5] = (f2 >> 8) & 0xff;
|
|
mc->mc_mailbox[0x6] = (f2 >> 16) & 0xff;
|
|
mc->mc_mailbox[0x7] = f3;
|
|
mc->mc_mailbox[0x8] = f4 & 0xff;
|
|
mc->mc_mailbox[0x9] = (f4 >> 8) & 0xff;
|
|
mc->mc_mailbox[0xa] = (f4 >> 16) & 0xff;
|
|
mc->mc_mailbox[0xb] = (f4 >> 24) & 0xff;
|
|
mc->mc_mailbox[0xc] = f5;
|
|
}
|
|
|
|
static __inline void
|
|
mlx_make_type2(struct mlx_command *mc,
|
|
u_int8_t code,
|
|
u_int8_t f1,
|
|
u_int8_t f2,
|
|
u_int8_t f3,
|
|
u_int8_t f4,
|
|
u_int8_t f5,
|
|
u_int8_t f6,
|
|
u_int32_t f7,
|
|
u_int8_t f8)
|
|
{
|
|
mc->mc_mailbox[0x0] = code;
|
|
mc->mc_mailbox[0x2] = f1;
|
|
mc->mc_mailbox[0x3] = f2;
|
|
mc->mc_mailbox[0x4] = f3;
|
|
mc->mc_mailbox[0x5] = f4;
|
|
mc->mc_mailbox[0x6] = f5;
|
|
mc->mc_mailbox[0x7] = f6;
|
|
mc->mc_mailbox[0x8] = f7 & 0xff;
|
|
mc->mc_mailbox[0x9] = (f7 >> 8) & 0xff;
|
|
mc->mc_mailbox[0xa] = (f7 >> 16) & 0xff;
|
|
mc->mc_mailbox[0xb] = (f7 >> 24) & 0xff;
|
|
mc->mc_mailbox[0xc] = f8;
|
|
}
|
|
|
|
static __inline void
|
|
mlx_make_type3(struct mlx_command *mc,
|
|
u_int8_t code,
|
|
u_int8_t f1,
|
|
u_int8_t f2,
|
|
u_int16_t f3,
|
|
u_int8_t f4,
|
|
u_int8_t f5,
|
|
u_int32_t f6,
|
|
u_int8_t f7)
|
|
{
|
|
mc->mc_mailbox[0x0] = code;
|
|
mc->mc_mailbox[0x2] = f1;
|
|
mc->mc_mailbox[0x3] = f2;
|
|
mc->mc_mailbox[0x4] = f3 & 0xff;
|
|
mc->mc_mailbox[0x5] = (f3 >> 8) & 0xff;
|
|
mc->mc_mailbox[0x6] = f4;
|
|
mc->mc_mailbox[0x7] = f5;
|
|
mc->mc_mailbox[0x8] = f6 & 0xff;
|
|
mc->mc_mailbox[0x9] = (f6 >> 8) & 0xff;
|
|
mc->mc_mailbox[0xa] = (f6 >> 16) & 0xff;
|
|
mc->mc_mailbox[0xb] = (f6 >> 24) & 0xff;
|
|
mc->mc_mailbox[0xc] = f7;
|
|
}
|
|
|
|
static __inline void
|
|
mlx_make_type4(struct mlx_command *mc,
|
|
u_int8_t code,
|
|
u_int16_t f1,
|
|
u_int32_t f2,
|
|
u_int32_t f3,
|
|
u_int8_t f4)
|
|
{
|
|
mc->mc_mailbox[0x0] = code;
|
|
mc->mc_mailbox[0x2] = f1 & 0xff;
|
|
mc->mc_mailbox[0x3] = (f1 >> 8) & 0xff;
|
|
mc->mc_mailbox[0x4] = f2 & 0xff;
|
|
mc->mc_mailbox[0x5] = (f2 >> 8) & 0xff;
|
|
mc->mc_mailbox[0x6] = (f2 >> 16) & 0xff;
|
|
mc->mc_mailbox[0x7] = (f2 >> 24) & 0xff;
|
|
mc->mc_mailbox[0x8] = f3 & 0xff;
|
|
mc->mc_mailbox[0x9] = (f3 >> 8) & 0xff;
|
|
mc->mc_mailbox[0xa] = (f3 >> 16) & 0xff;
|
|
mc->mc_mailbox[0xb] = (f3 >> 24) & 0xff;
|
|
mc->mc_mailbox[0xc] = f4;
|
|
}
|
|
|
|
static __inline void
|
|
mlx_make_type5(struct mlx_command *mc,
|
|
u_int8_t code,
|
|
u_int8_t f1,
|
|
u_int8_t f2,
|
|
u_int32_t f3,
|
|
u_int32_t f4,
|
|
u_int8_t f5)
|
|
{
|
|
mc->mc_mailbox[0x0] = code;
|
|
mc->mc_mailbox[0x2] = f1;
|
|
mc->mc_mailbox[0x3] = f2;
|
|
mc->mc_mailbox[0x4] = f3 & 0xff;
|
|
mc->mc_mailbox[0x5] = (f3 >> 8) & 0xff;
|
|
mc->mc_mailbox[0x6] = (f3 >> 16) & 0xff;
|
|
mc->mc_mailbox[0x7] = (f3 >> 24) & 0xff;
|
|
mc->mc_mailbox[0x8] = f4 & 0xff;
|
|
mc->mc_mailbox[0x9] = (f4 >> 8) & 0xff;
|
|
mc->mc_mailbox[0xa] = (f4 >> 16) & 0xff;
|
|
mc->mc_mailbox[0xb] = (f4 >> 24) & 0xff;
|
|
mc->mc_mailbox[0xc] = f5;
|
|
}
|