📄 ata.c
字号:
/* * ata.c * * ATA RTEMS driver. ATA driver is hardware independant implementation of * ATA-2 standart, working draft X3T10/0948D, revision 4c. ATA driver bases * on RTEMS IDE controller driver. * * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia * Authors: Eugeny S. Mints <Eugeny.Mints@oktet.ru> * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. * * $Id: ata.c,v 1.1.2.3 2003/09/04 18:46:04 joel Exp $ * */#include <errno.h>#include <chain.h>#include <assert.h>#include <string.h> /* for "memset" declaration */#include <rtems/diskdevs.h>#include <rtems/blkdev.h>#include <libchip/ide_ctrl_io.h>#include <libchip/ide_ctrl_cfg.h>#include "ata_internal.h"#include <libchip/ata.h>#define DEBUG#ifdef DEBUG#include <stdio.h>#endif#define SAFE#ifdef SAFEtypedef rtems_mode preemption_key;#define DISABLE_PREEMPTION(key) \ do { \ rtems_task_mode(RTEMS_NO_PREEMPT, RTEMS_PREEMPT_MASK, &(key)); \ } while (0)#define ENABLE_PREEMPTION(key) \ do { \ rtems_mode temp; \ rtems_task_mode((key), RTEMS_PREEMPT_MASK, &temp); \ } while (0)#else typedef boolean preemption_key;#define DISABLE_PREEMPTION(key) \ do { \ (key) = _Thread_Executing->is_preemptible; \ _Thread_Executing->is_preemptible = 0; \ } while (0) #define ENABLE_PREEMPTION(key) \ do { \ _Thread_Executing->is_preemptible = (key); \ if (_Thread_Evaluate_mode()) \ _Thread_Dispatch(); \ } while (0)#endif/* FIXME: case if ATA device is FLASH device need more attention */#undef ATA_DEV_IS_FLASH_DISK/* Block device request with a single buffer provided */typedef struct blkdev_request1 { blkdev_request req; blkdev_sg_buffer sg[1];} blkdev_request1;/* Array indexed by controllers minor number */static ata_ide_ctrl_t ata_ide_ctrls[IDE_CTRL_MAX_MINOR_NUMBER];/* * Mapping from ATA-minor numbers to * controller-minor and device on this controller. */static ata_ide_dev_t ata_devs[2 * IDE_CTRL_MAX_MINOR_NUMBER];static int ata_devs_number;/* Flag meaning that ATA driver has already been initialized */static rtems_boolean ata_initialized = FALSE;/* task and queue used for asynchronous I/O operations */static rtems_id ata_task_id;static rtems_id ata_queue_id;/* Mapping of interrupt vectors to devices */static Chain_Control ata_int_vec[ATA_MAX_RTEMS_INT_VEC_NUMBER + 1]; static voidata_process_request(rtems_device_minor_number ctrl_minor);static voidata_process_request_on_init_phase(rtems_device_minor_number ctrl_minor, ata_req_t *areq);static void ata_add_to_controller_queue(rtems_device_minor_number ctrl_minor, ata_req_t *areq);/* * read/write, open/close and ioctl are provided by general block device * driver. Only initialization and ata-specific ioctl are here. *//* ata_io_data_request -- * Form read/write request for an ATA device and enqueue it to * IDE controller. * * PARAMETERS: * device - device identifier * req - read/write request from block device driver * * RETURNS: * RTEMS_SUCCESSFUL on success, or error code if * error occured */static rtems_status_codeata_io_data_request(dev_t device, blkdev_request *req){ ata_req_t *areq; /* ATA request */ rtems_device_minor_number rel_minor; /* relative minor which indexes * ata_devs array */ rtems_device_minor_number ctrl_minor; unsigned8 dev; rel_minor = (rtems_filesystem_dev_minor_t(device)) / ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE; /* get controller which serves the ATA device */ ctrl_minor = ata_devs[rel_minor].ctrl_minor; /* get ATA device identifier (0 or 1) */ dev = ata_devs[rel_minor].device; areq = malloc(sizeof(ata_req_t)); if (areq == NULL) { return RTEMS_NO_MEMORY; } areq->breq = req; areq->cnt = req->count; areq->cbuf = 0; areq->pos = 0; /* set up registers masks */ areq->regs.to_write = ATA_REGISTERS_POSITION; areq->regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_STATUS); /* choose device on the controller for which the command will be issued */ areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] = (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS); /* Find ATA command and its type */ if (ATA_DEV_INFO(ctrl_minor, dev).mode_active & ATA_MODES_DMA) { /* XXX: never has been tested */ areq->type = ATA_COMMAND_TYPE_DMA; if (req->req == BLKDEV_REQ_READ) areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_READ_DMA; else areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_WRITE_DMA; } else { if (req->req == BLKDEV_REQ_READ) { areq->type = ATA_COMMAND_TYPE_PIO_IN; /* * choose command to issue: if the number of blocks to be * exchanged is greater then 1 and for ATA command READ MULTIPLE * data block consists of more then 1 sector choose READ MULTIPLE * otherwise READ SECTORS */ areq->regs.regs[IDE_REGISTER_COMMAND] = ((ATA_DEV_INFO(ctrl_minor, dev).max_multiple) && (req->count > 1) && (ATA_DEV_INFO(ctrl_minor, dev).current_multiple > 1)) ? ATA_COMMAND_READ_MULTIPLE : ATA_COMMAND_READ_SECTORS; } else { areq->type = ATA_COMMAND_TYPE_PIO_OUT; /* * choose command to issue: if the number of blocks to be * exchanged is greater then 1 and for ATA command WRITE MULTIPLE * data block consists of more then 1 sector choose WRITE MULTIPLE * otherwise WRITE SECTORS */ areq->regs.regs[IDE_REGISTER_COMMAND] = ((ATA_DEV_INFO(ctrl_minor, dev).max_multiple) && (req->count > 1) && (ATA_DEV_INFO(ctrl_minor, dev).current_multiple > 1)) ? ATA_COMMAND_WRITE_MULTIPLE : ATA_COMMAND_WRITE_SECTORS; } } /* * Fill position registers */ if (ATA_DEV_INFO(ctrl_minor, dev).lba_avaible) { areq->regs.regs[IDE_REGISTER_LBA0] = (unsigned8)req->start; areq->regs.regs[IDE_REGISTER_LBA1] = (unsigned8)(req->start >> 8); areq->regs.regs[IDE_REGISTER_LBA2] = (unsigned8)(req->start >> 16); areq->regs.regs[IDE_REGISTER_LBA3] |= (unsigned8) (req->start >> 24); areq->regs.regs[IDE_REGISTER_LBA3] |= IDE_REGISTER_LBA3_L; } else { unsigned32 count = req->start; areq->regs.regs[IDE_REGISTER_SECTOR_NUMBER] = (count % ATA_DEV_INFO(ctrl_minor, dev).sectors) + 1; /* now count = number of tracks: */ count /= ATA_DEV_INFO(ctrl_minor, dev).sectors; areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] |= (count / ATA_DEV_INFO(ctrl_minor, dev).cylinders); /* now count = number of cylinders */ count %= ATA_DEV_INFO(ctrl_minor, dev).cylinders; areq->regs.regs[IDE_REGISTER_CYLINDER_LOW] = (unsigned8)count; areq->regs.regs[IDE_REGISTER_CYLINDER_HIGH] = (unsigned8)(count >> 8); areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] &= ~IDE_REGISTER_DEVICE_HEAD_L; } /* fill sector count register */ areq->regs.regs[IDE_REGISTER_SECTOR_COUNT] = areq->breq->count; /* add request to the queue of awaiting requests to the controller */ ata_add_to_controller_queue(ctrl_minor, areq); return RTEMS_SUCCESSFUL;}/* ata_non_data_request -- * Form and serve request of NON DATA type for an ATA device. * Processing of NON DATA request is SYNChronous operation. * * PARAMETERS: * device - device identifier * cmd - command * argp - arguments for command * * RETURNS: * RTEMS_SUCCESSFUL on success, or error code if * error occured */static rtems_status_codeata_non_data_request(dev_t device, int cmd, void *argp){ rtems_status_code rc; ata_req_t *areq; /* ATA request */ rtems_device_minor_number rel_minor; /* relative minor which indexes * ata_devs array */ rtems_device_minor_number ctrl_minor; unsigned8 dev; ata_queue_msg_t msg; rel_minor = (rtems_filesystem_dev_minor_t(device)) / ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE; /* get controller which serves the ATA device */ ctrl_minor = ata_devs[rel_minor].ctrl_minor; /* get ATA device identifier (0 or 1) */ dev = ata_devs[rel_minor].device; /* form the request */ areq = malloc(sizeof(ata_req_t)); if (areq == NULL) { return RTEMS_NO_MEMORY; } memset(areq, 0, sizeof(ata_req_t)); areq->type = ATA_COMMAND_TYPE_NON_DATA; areq->regs.to_write = ATA_REGISTERS_VALUE(IDE_REGISTER_COMMAND); areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] |= (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS); areq->breq = NULL; areq->regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_ERROR); /* * depending on command fill command register and additional registers * which are needed for command execution */ switch(cmd) { case ATAIO_SET_MULTIPLE_MODE: areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_SET_MULTIPLE_MODE; areq->regs.to_write |= ATA_REGISTERS_VALUE(IDE_REGISTER_SECTOR_COUNT); areq->regs.regs[IDE_REGISTER_SECTOR_COUNT] = *(unsigned8 *)argp; break; default: free(areq); return RTEMS_INVALID_NUMBER; break; } rc = rtems_semaphore_create(rtems_build_name('I', 'D', 'E', 'S'), 0, RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0, &(areq->sema)); if (rc != RTEMS_SUCCESSFUL) { free(areq); return rc; } ata_add_to_controller_queue(ctrl_minor, areq); /* wait for request processing... */ rc = rtems_semaphore_obtain(areq->sema, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (rc != RTEMS_SUCCESSFUL) { free(areq); return rc; } rtems_semaphore_delete(areq->sema); /* * if no error occurred and if necessary, update internal ata driver data * structures to reflect changes (in device configuration, for example) */ if (areq->status == RTEMS_SUCCESSFUL) { switch(cmd) { case ATAIO_SET_MULTIPLE_MODE: ATA_DEV_INFO(ctrl_minor, dev).current_multiple = *(unsigned8 *)argp; break; default: rc = RTEMS_INVALID_NUMBER; break; } } else { /* XXX: should be correct error processing: for ex, may be * ABRT and then we should return RTEMS_NOT_IMPLEMENTED */ rc = RTEMS_IO_ERROR; } /* tell ata driver that controller ready to serve next request */ ATA_SEND_EVT(msg, ATA_MSG_SUCCESS_EVT, ctrl_minor, 0); return rc;}/* ata_process_request -- * Get first request from controller's queue and process it. * * PARAMETERS: * ctrl_minor - controller identifier * * RETURNS: * NONE */static voidata_process_request(rtems_device_minor_number ctrl_minor){ ata_req_t *areq; unsigned16 byte; /* emphasize that only 8 low bits is meaningful */ ata_queue_msg_t msg; unsigned8 i, dev; unsigned16 val; unsigned16 data_bs; /* the number of 512-bytes sectors in one * data block */ ISR_Level level; /* if no requests to controller then do nothing */ if (Chain_Is_empty(&ata_ide_ctrls[ctrl_minor].reqs)) return; /* get first request in the controller's queue */ _ISR_Disable(level); areq = (ata_req_t *)(ata_ide_ctrls[ctrl_minor].reqs.first); _ISR_Enable(level); /* get ATA device identifier (0 or 1) */ dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] & IDE_REGISTER_DEVICE_HEAD_DEV; /* get data block size */ data_bs = ATA_DEV_INFO(ctrl_minor, dev).current_multiple ? ATA_DEV_INFO(ctrl_minor, dev).current_multiple : 1; /* execute device select protocol */ ide_controller_write_register(ctrl_minor, IDE_REGISTER_DEVICE_HEAD, areq->regs.regs[IDE_REGISTER_DEVICE_HEAD]); do { ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &byte); } while ((byte & IDE_REGISTER_STATUS_BSY) || (!(byte & IDE_REGISTER_STATUS_DRDY))); /* fill in all necessary registers on the controller */ for (i=0; i< ATA_MAX_CMD_REG_OFFSET; i++) { unsigned32 reg = (1 << i); if (areq->regs.to_write & reg) ide_controller_write_register(ctrl_minor, i, areq->regs.regs[i]); } /* continue to execute ATA protocols depending on type of request */ if (areq->type == ATA_COMMAND_TYPE_PIO_OUT) { do {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -