35d422cbd9352c562eb198e3e97773fbec47e680
[profile/ivi/syslinux.git] / com32 / hdt / hdt-ata.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2009 Erwan Velu - All Rights Reserved
4  *
5  *   Permission is hereby granted, free of charge, to any person
6  *   obtaining a copy of this software and associated documentation
7  *   files (the "Software"), to deal in the Software without
8  *   restriction, including without limitation the rights to use,
9  *   copy, modify, merge, publish, distribute, sublicense, and/or
10  *   sell copies of the Software, and to permit persons to whom
11  *   the Software is furnished to do so, subject to the following
12  *   conditions:
13  *
14  *   The above copyright notice and this permission notice shall
15  *   be included in all copies or substantial portions of the Software.
16  *
17  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24  *   OTHER DEALINGS IN THE SOFTWARE.
25  *
26  * -----------------------------------------------------------------------
27  */
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <console.h>
33
34 #include "com32io.h"
35 #include "hdt-ata.h"
36
37 #ifdef ATA
38 /**
39  *      ata_id_string - Convert IDENTIFY DEVICE page into string
40  *      @id: IDENTIFY DEVICE results we will examine
41  *      @s: string into which data is output
42  *      @ofs: offset into identify device page
43  *      @len: length of string to return. must be an even number.
44  *
45  *      The strings in the IDENTIFY DEVICE page are broken up into
46  *      16-bit chunks.  Run through the string, and output each
47  *      8-bit chunk linearly, regardless of platform.
48  *
49  *      LOCKING:
50  *      caller.
51  */
52 void ata_id_string(const uint16_t * id, unsigned char *s,
53        unsigned int ofs, unsigned int len)
54 {
55   unsigned int c;
56
57   while (len > 0) {
58     c = id[ofs] >> 8;
59     *s = c;
60     s++;
61
62     c = id[ofs] & 0xff;
63     *s = c;
64     s++;
65
66     ofs++;
67     len -= 2;
68   }
69 }
70
71 /**
72  *      ata_id_c_string - Convert IDENTIFY DEVICE page into C string
73  *      @id: IDENTIFY DEVICE results we will examine
74  *      @s: string into which data is output
75  *      @ofs: offset into identify device page
76  *      @len: length of string to return. must be an odd number.
77  *
78  *      This function is identical to ata_id_string except that it
79  *      trims trailing spaces and terminates the resulting string with
80  *      null.  @len must be actual maximum length (even number) + 1.
81  *
82  *      LOCKING:
83  *      caller.
84  */
85 void ata_id_c_string(const uint16_t * id, unsigned char *s,
86          unsigned int ofs, unsigned int len)
87 {
88   unsigned char *p;
89
90   //WARN_ON(!(len & 1));
91
92   ata_id_string(id, s, ofs, len - 1);
93
94   p = s + strnlen(s, len - 1);
95   while (p > s && p[-1] == ' ')
96     p--;
97   *p = '\0';
98 }
99 #endif
100
101 /**
102  * Call int 13h, but with retry on failure.  Especially floppies need this.
103  */
104 int int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
105 {
106   int retry = 6;    /* Number of retries */
107   com32sys_t tmpregs;
108
109   if (!outreg)
110     outreg = &tmpregs;
111
112   while (retry--) {
113     __intcall(0x13, inreg, outreg);
114     if (!(outreg->eflags.l & EFLAGS_CF))
115       return 0; /* CF=0, OK */
116   }
117
118   return -1;    /* Error */
119 }
120
121 /* Display CPU registers for debugging purposes */
122 void printregs(const com32sys_t * r)
123 {
124   printf("eflags = %08x  ds = %04x  es = %04x  fs = %04x  gs = %04x\n"
125          "eax = %08x  ebx = %08x  ecx = %08x  edx = %08x\n"
126          "ebp = %08x  esi = %08x  edi = %08x  esp = %08x\n",
127          r->eflags.l, r->ds, r->es, r->fs, r->gs,
128          r->eax.l, r->ebx.l, r->ecx.l, r->edx.l,
129          r->ebp.l, r->esi.l, r->edi.l, r->_unused_esp.l);
130 }
131
132 /* Try to get information for a given disk */
133 int get_disk_params(int disk, struct diskinfo *disk_info)
134 {
135   static com32sys_t getparm, parm, getebios, ebios, inreg, outreg;
136   struct device_parameter dp;
137 #ifdef ATA
138   struct ata_identify_device aid;
139 #endif
140
141   memset(&(disk_info[disk]), 0, sizeof(struct diskinfo));
142
143   disk_info[disk].disk = disk;
144   disk_info[disk].ebios = disk_info[disk].cbios = 0;
145
146   /* Sending int 13h func 41h to query EBIOS information */
147   memset(&getebios, 0, sizeof(com32sys_t));
148   memset(&ebios, 0, sizeof(com32sys_t));
149
150   /* Get EBIOS support */
151   getebios.eax.w[0] = 0x4100;
152   getebios.ebx.w[0] = 0x55aa;
153   getebios.edx.b[0] = disk;
154   getebios.eflags.b[0] = 0x3; /* CF set */
155
156   __intcall(0x13, &getebios, &ebios);
157
158   /* Detecting EDD support */
159   if (!(ebios.eflags.l & EFLAGS_CF) &&
160       ebios.ebx.w[0] == 0xaa55 && (ebios.ecx.b[0] & 1)) {
161     disk_info[disk].ebios = 1;
162     switch (ebios.eax.b[1]) {
163     case 32:
164       strlcpy(disk_info[disk].edd_version, "1.0", 3);
165       break;
166     case 33:
167       strlcpy(disk_info[disk].edd_version, "1.1", 3);
168       break;
169     case 48:
170       strlcpy(disk_info[disk].edd_version, "3.0", 3);
171       break;
172     default:
173       strlcpy(disk_info[disk].edd_version, "0", 1);
174       break;
175     }
176   }
177   /* Get disk parameters -- really only useful for
178    * hard disks, but if we have a partitioned floppy
179    * it's actually our best chance...
180    */
181   memset(&getparm, 0, sizeof(com32sys_t));
182   memset(&parm, 0, sizeof(com32sys_t));
183   getparm.eax.b[1] = 0x08;
184   getparm.edx.b[0] = disk;
185
186   __intcall(0x13, &getparm, &parm);
187
188   if (parm.eflags.l & EFLAGS_CF)
189     return disk_info[disk].ebios ? 0 : -1;
190
191   disk_info[disk].heads = parm.edx.b[1] + 1;
192   disk_info[disk].sectors_per_track = parm.ecx.b[0] & 0x3f;
193   if (disk_info[disk].sectors_per_track == 0) {
194     disk_info[disk].sectors_per_track = 1;
195   } else {
196     disk_info[disk].cbios = 1;  /* Valid geometry */
197   }
198
199 /* FIXME: memset to 0 make it fails
200  * memset(__com32.cs_bounce, 0, sizeof(struct device_pairameter)); */
201   memset(&dp, 0, sizeof(struct device_parameter));
202   memset(&inreg, 0, sizeof(com32sys_t));
203
204   /* Requesting Extended Read Drive Parameters via int13h func 48h */
205   inreg.esi.w[0] = OFFS(__com32.cs_bounce);
206   inreg.ds = SEG(__com32.cs_bounce);
207   inreg.eax.w[0] = 0x4800;
208   inreg.edx.b[0] = disk;
209
210   __intcall(0x13, &inreg, &outreg);
211
212   /* Saving bounce buffer before anything corrupt it */
213   memcpy(&dp, __com32.cs_bounce, sizeof(struct device_parameter));
214
215   if (outreg.eflags.l & EFLAGS_CF) {
216     more_printf("Disk 0x%X doesn't supports EDD 3.0\n", disk);
217     return -1;
218   }
219
220   /* Copying result to the disk_info structure
221    * host_bus_type, interface_type, sectors & cylinders */
222   snprintf(disk_info[disk].host_bus_type,
223      sizeof disk_info[disk].host_bus_type, "%c%c%c%c",
224      dp.host_bus_type[0], dp.host_bus_type[1], dp.host_bus_type[2],
225      dp.host_bus_type[3]);
226   snprintf(disk_info[disk].interface_type,
227      sizeof disk_info[disk].interface_type, "%c%c%c%c%c%c%c%c",
228      dp.interface_type[0], dp.interface_type[1],
229      dp.interface_type[2], dp.interface_type[3],
230      dp.interface_type[4], dp.interface_type[5],
231      dp.interface_type[6], dp.interface_type[7]);
232   disk_info[disk].sectors = dp.sectors;
233   disk_info[disk].cylinders = dp.cylinders;
234
235   /*FIXME: we have to find a way to grab the model & fw
236    * We do put dummy data until we found a solution */
237   snprintf(disk_info[disk].aid.model, sizeof disk_info[disk].aid.model,
238      "0x%X", disk);
239   snprintf(disk_info[disk].aid.fw_rev, sizeof disk_info[disk].aid.fw_rev,
240      "%s", "N/A");
241   snprintf(disk_info[disk].aid.serial_no,
242      sizeof disk_info[disk].aid.serial_no, "%s", "N/A");
243
244   /* Useless stuff before I figure how to send ata packets */
245 #ifdef ATA
246   memset(__com32.cs_bounce, 0, sizeof(struct device_parameter));
247   memset(&aid, 0, sizeof(struct ata_identify_device));
248   memset(&inreg, 0, sizeof inreg);
249   inreg.ebx.w[0] = OFFS(__com32.cs_bounce + 1024);
250   inreg.es = SEG(__com32.cs_bounce + 1024);
251   inreg.eax.w[0] = 0x2500;
252   inreg.edx.b[0] = disk;
253
254   __intcall(0x13, &inreg, &outreg);
255
256   memcpy(&aid, __com32.cs_bounce, sizeof(struct ata_identify_device));
257
258   if (outreg.eflags.l & EFLAGS_CF) {
259     more_printf("Disk 0x%X: Failed to Identify Device\n", disk);
260     //FIXME
261     return 0;
262   }
263 //   ata_id_c_string(aid, disk_info[disk].fwrev, ATA_ID_FW_REV, sizeof(disk_info[disk].fwrev));
264 //   ata_id_c_string(aid, disk_info[disk].model, ATA_ID_PROD,  sizeof(disk_info[disk].model));
265
266   char buff[sizeof(struct ata_identify_device)];
267   memcpy(buff, &aid, sizeof(struct ata_identify_device));
268   for (int j = 0; j < sizeof(struct ata_identify_device); j++)
269     more_printf("model=|%c|\n", buff[j]);
270   more_printf("Disk 0x%X : %s %s %s\n", disk, aid.model, aid.fw_rev,
271          aid.serial_no);
272 #endif
273
274   return 0;
275 }