kern

view src/ata.c @ 82:8b92b0c1c220

added decimal digits to the disk size output
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 06 Dec 2011 13:45:32 +0200
parents 9c979413cfbf
children 4ef83db5f4cd
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <inttypes.h>
5 #include <assert.h>
6 #include "ata.h"
7 #include "intr.h"
8 #include "asmops.h"
10 /* registers */
11 #define REG_DATA 0 /* R/W */
12 #define REG_ERROR 1 /* R */
13 #define REG_FEATURES 1 /* W */
14 #define REG_COUNT 2 /* R/W */
15 #define REG_LBA0 3 /* R/W */
16 #define REG_LBA1 4 /* R/W */
17 #define REG_LBA2 5 /* R/W */
18 #define REG_DEVICE 6 /* R/W */
19 #define REG_CMD 7 /* W */
20 #define REG_STATUS 7 /* R */
22 #define REG_CTL 518
23 #define REG_ALTSTAT 518
25 /* status bit fields */
26 #define ST_ERR (1 << 0)
27 #define ST_DRQ (1 << 3)
28 #define ST_DRDY (1 << 6)
29 #define ST_BSY (1 << 7)
31 /* device select bit in control register */
32 #define DEV_SEL(x) (((x) & 1) << 4)
34 /* ATA commands */
35 #define CMD_IDENTIFY 0xec
38 struct device {
39 int id; /* id of the device on its ATA interface (0 master, 1 slave) */
40 int iface; /* ATA interface for this device (0 or 1) */
41 int port_base; /* interface I/O port base */
43 uint32_t nsect_lba;
44 uint64_t nsect_lba48;
45 };
48 static int identify(struct device *dev, int iface, int id);
49 static void select_dev(struct device *dev);
50 static int wait_busy(struct device *dev);
51 static int wait_drq(struct device *dev);
52 static void read_data(struct device *dev, void *buf);
53 static inline uint8_t read_reg8(struct device *dev, int reg);
54 static inline uint16_t read_reg16(struct device *dev, int reg);
55 static inline void write_reg8(struct device *dev, int reg, uint8_t val);
56 static inline void write_reg16(struct device *dev, int reg, uint16_t val);
57 static void ata_intr(int inum);
58 static void *atastr(void *res, void *src, int n);
59 static char *size_str(uint64_t nsect, char *buf);
61 /* last drive selected on each bus */
62 static int drvsel[2] = {-1, -1};
64 /* 4 possible devices: 2 ATA interfaces with 2 devices each.
65 * this will never change unless we start querying the PCI config space
66 * for additional drives (in which case this whole init code must be
67 * rewritten anyway), but I like it spelt out like this.
68 */
69 #define MAX_IFACES 2
70 #define MAX_DEV (MAX_IFACES * 2)
71 static struct device dev[MAX_DEV];
74 void init_ata(void)
75 {
76 int i;
78 interrupt(IRQ_TO_INTR(15), ata_intr);
80 for(i=0; i<MAX_DEV; i++) {
81 int iface = i / MAX_IFACES;
82 int id = i % MAX_IFACES;
84 if(identify(dev + i, iface, id) == -1) {
85 dev[i].id = -1;
86 }
87 }
88 }
90 static int identify(struct device *dev, int iface, int id)
91 {
92 /* base address of the two ATA interfaces */
93 static const int port_base[] = {0x1f0, 0x170};
94 unsigned char st;
95 uint16_t *info;
96 char textbuf[42]; /* at most we need 40 chars for ident strings */
98 dev->id = id;
99 dev->iface = iface;
100 dev->port_base = port_base[iface];
102 /* a status of 0xff means there's no drive on the interface */
103 if((st = read_reg8(dev, REG_ALTSTAT)) == 0xff) {
104 return -1;
105 }
107 select_dev(dev);
108 /* wait a bit to allow the device time to respond */
109 iodelay(); iodelay(); iodelay(); iodelay();
111 write_reg8(dev, REG_CMD, CMD_IDENTIFY);
113 if(!(st = read_reg8(dev, REG_ALTSTAT)) || (st & ST_ERR)) {
114 /* does not exist */
115 return -1;
116 }
117 if(wait_busy(dev) == -1) {
118 /* got ST_ERR, not ATA */
119 return -1;
120 }
122 info = malloc(512);
123 assert(info);
125 /* read the device information */
126 read_data(dev, info);
128 /* print model and serial */
129 printf("- found ata drive (%d,%d): %s\n", dev->iface, dev->id, atastr(textbuf, info + 27, 40));
130 printf(" s/n: %s\n", atastr(textbuf, info + 10, 20));
132 dev->nsect_lba = *(uint32_t*)(info + 60);
133 dev->nsect_lba48 = *(uint64_t*)(info + 100) & 0xffffffffffffull;
135 if(!dev->nsect_lba) {
136 printf(" drive does not support LBA, ignoring!\n");
137 free(info);
138 return -1;
139 }
141 if(dev->nsect_lba48) {
142 size_str(dev->nsect_lba48, textbuf);
143 } else {
144 size_str(dev->nsect_lba, textbuf);
145 }
146 printf(" size: %s\n", textbuf);
148 free(info);
149 return 0;
150 }
152 static void select_dev(struct device *dev)
153 {
154 /* if this is the currently selected device, thy work is done */
155 if(drvsel[dev->iface] == dev->id)
156 return;
158 /* wait for BSY and DRQ to clear */
159 while(read_reg8(dev, REG_ALTSTAT) & (ST_BSY | ST_DRQ));
161 /* set the correct device bit to the device register */
162 write_reg8(dev, REG_DEVICE, DEV_SEL(dev->id));
163 }
165 static int wait_busy(struct device *dev)
166 {
167 unsigned char st;
169 do {
170 st = read_reg8(dev, REG_ALTSTAT);
171 } while((st & ST_BSY) && !(st & ST_ERR));
173 return st & ST_ERR ? -1 : 0;
174 }
176 static int wait_drq(struct device *dev)
177 {
178 unsigned char st;
180 do {
181 st = read_reg8(dev, REG_ALTSTAT);
182 } while(!(st & (ST_DRQ | ST_ERR)));
184 return st & ST_ERR ? -1 : 0;
185 }
187 static void read_data(struct device *dev, void *buf)
188 {
189 int i;
190 uint16_t *ptr = buf;
192 /* wait for the data request from the drive */
193 wait_drq(dev);
195 /* ready to transfer */
196 for(i=0; i<256; i++) {
197 *ptr++ = read_reg16(dev, REG_DATA);
198 }
199 }
201 static inline uint8_t read_reg8(struct device *dev, int reg)
202 {
203 uint8_t val;
204 inb(val, dev->port_base + reg);
205 return val;
206 }
208 static inline uint16_t read_reg16(struct device *dev, int reg)
209 {
210 uint16_t val;
211 inw(val, dev->port_base + reg);
212 return val;
213 }
215 static inline void write_reg8(struct device *dev, int reg, uint8_t val)
216 {
217 outb(val, dev->port_base + reg);
218 }
220 static inline void write_reg16(struct device *dev, int reg, uint16_t val)
221 {
222 outw(val, dev->port_base + reg);
223 }
225 static void ata_intr(int inum)
226 {
227 printf("ATA interrupt\n");
228 }
230 static void *atastr(void *res, void *src, int n)
231 {
232 int i;
233 uint16_t *sptr = (uint16_t*)src;
234 char *dptr = res;
236 for(i=0; i<n/2; i++) {
237 *dptr++ = (*sptr & 0xff00) >> 8;
238 *dptr++ = *sptr++ & 0xff;
239 }
240 *dptr = 0;
241 return res;
242 }
244 static char *size_str(uint64_t nsect, char *buf)
245 {
246 static const char *suffix[] = {"kb", "mb", "gb", "tb", "pb", 0};
247 int i;
248 unsigned int rem;
250 /* start with kilobytes */
251 nsect /= 2;
253 for(i=0; nsect >= 1024 && suffix[i + 1]; i++) {
254 rem = nsect % 1024;
255 nsect /= 1024;
256 }
257 sprintf(buf, "%u.%u%s", (unsigned int)nsect, 100 * rem / 1024, suffix[i]);
258 return buf;
259 }