Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / arch / ppc / mol / osi-scsi.c
1 /*
2  *   Creation Date: <2003/12/11 21:23:54 samuel>
3  *   Time-stamp: <2004/01/07 19:38:45 samuel>
4  *
5  *      <osi-scsi.c>
6  *
7  *      SCSI device node
8  *
9  *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
10  *
11  *   This program is free software; you can redistribute it and/or
12  *   modify it under the terms of the GNU General Public License
13  *   version 2
14  *
15  */
16
17 #include "config.h"
18 #include "libopenbios/bindings.h"
19 #include "mol/mol.h"
20 #include "scsi_sh.h"
21 #include "osi_calls.h"
22
23 #define MAX_TARGETS     32
24
25 typedef struct {
26         int             probed;
27         int             valid;          /* a useable device found */
28
29         int             is_cd;
30         int             blocksize;
31 } target_info_t;
32
33 static target_info_t    scsi_devs[ MAX_TARGETS ];
34
35 typedef struct {
36         int             target;
37         target_info_t   *info;
38 } instance_data_t;
39
40
41 DECLARE_NODE( scsi, INSTALL_OPEN, sizeof(instance_data_t),
42               "/pci/pci-bridge/mol-scsi/sd", "/mol/mol-scsi/sd" );
43
44
45 static int
46 scsi_cmd_( instance_data_t *sd, const char *cmd, int cmdlen, char *dest,
47            int len, int prelen, int postlen )
48 {
49         char prebuf[4096], postbuf[4096];
50         scsi_req_t r[2];        /* the [2] is a hack to get space for the sg-list */
51         char sb[32];
52
53         /* memset( dest, 0, len ); */
54
55         if( (unsigned int)prelen > sizeof(prebuf) || (unsigned int)postlen > sizeof(postbuf) ) {
56                 printk("bad pre/post len %d %d\n", prelen, postlen );
57                 return 1;
58         }
59
60         memset( r, 0, sizeof(r[0]) );
61         r->lun = 0;
62         r->target = sd->target;
63         r->is_write = 0;
64         memcpy( r->cdb, cmd, cmdlen );
65         r->client_addr = (int)&r;
66         r->cdb_len = cmdlen;
67         r->sense[0].base = (int)&sb;
68         r->sense[0].size = sizeof(sb);
69         r->size = prelen + len + postlen;
70         r->n_sg = 3;
71         r->sglist.n_el = 3;
72         r->sglist.vec[0].base = (int)prebuf;
73         r->sglist.vec[0].size = prelen;
74         r->sglist.vec[1].base = (int)dest;
75         r->sglist.vec[1].size = len;
76         r->sglist.vec[2].base = (int)postbuf;
77         r->sglist.vec[2].size = postlen;
78
79         if( OSI_SCSISubmit((int)&r) ) {
80                 printk("OSI_SCSISubmit: error!\n");
81                 return 1;
82         }
83         while( !OSI_SCSIAck() )
84                 OSI_USleep( 10 );
85
86         if( r->adapter_status )
87                 return -1;
88         if( r->scsi_status )
89                 return ((sb[2] & 0xf) << 16) | (sb[12] << 8) | sb[13];
90         return 0;
91 }
92
93 static int
94 scsi_cmd( instance_data_t *sd, const char *cmd, int cmdlen )
95 {
96         return scsi_cmd_( sd, cmd, cmdlen, NULL, 0, 0, 0 );
97 }
98
99 /* ( buf blk nblks -- actual ) */
100 static void
101 scsi_read_blocks( instance_data_t *sd )
102 {
103         int nblks = POP();
104         int blk = POP();
105         char *dest = (char*)POP();
106         unsigned char cmd[10];
107         int len = nblks * sd->info->blocksize;
108
109         memset( dest, 0, len );
110
111         /* printk("READ: blk: %d length %d\n", blk, len ); */
112         memset( cmd, 0, sizeof(cmd) );
113         cmd[0] = 0x28; /* READ_10 */
114         cmd[2] = blk >> 24;
115         cmd[3] = blk >> 16;
116         cmd[4] = blk >> 8;
117         cmd[5] = blk;
118         cmd[7] = nblks >> 8;
119         cmd[8] = nblks;
120
121         if( scsi_cmd_(sd, cmd, 10, dest, len, 0, 0) ) {
122                 printk("read: scsi_cmd failed\n");
123                 RET( -1 );
124         }
125         PUSH( nblks );
126 }
127
128 static int
129 inquiry( instance_data_t *sd )
130 {
131         char inquiry_cmd[6] = { 0x12, 0, 0, 0, 32, 0 };
132         char start_stop_unit_cmd[6] = { 0x1b, 0, 0, 0, 1, 0 };
133         char test_unit_ready_cmd[6] = { 0x00, 0, 0, 0, 0, 0 };
134         char prev_allow_medium_removal[6] = { 0x1e, 0, 0, 0, 1, 0 };
135         char set_cd_speed_cmd[12] = { 0xbb, 0, 0xff, 0xff, 0xff, 0xff,
136                                       0, 0, 0, 0, 0, 0 };
137         target_info_t *info = &scsi_devs[sd->target];
138         char ret[32];
139         int i, sense;
140
141         if( sd->target >= MAX_TARGETS )
142                 return -1;
143         sd->info = info;
144
145         if( info->probed )
146                 return info->valid ? 0:-1;
147         info->probed = 1;
148
149         if( (sense=scsi_cmd_(sd, inquiry_cmd, 6, ret, 2, 0, 0)) ) {
150                 if( sense < 0 )
151                         return -1;
152                 printk("INQUIRY failed\n");
153                 return -1;
154         }
155
156         /* medium present? */
157         if( (scsi_cmd(sd, test_unit_ready_cmd, 6) >> 8) == 0x23a ) {
158                 printk("no media\n");
159                 return -1;
160         }
161
162         info->is_cd = 0;
163         info->blocksize = 512;
164
165         if( ret[0] == 5 /* CD/DVD */ ) {
166                 info->blocksize = 2048;
167                 info->is_cd = 1;
168
169                 scsi_cmd( sd, prev_allow_medium_removal, 6 );
170                 scsi_cmd( sd, set_cd_speed_cmd, 12 );
171                 scsi_cmd( sd, start_stop_unit_cmd, 6 );
172
173         } else if( ret[0] == 0 /* DISK */ ) {
174                 scsi_cmd( sd, test_unit_ready_cmd, 6 );
175                 scsi_cmd( sd, start_stop_unit_cmd, 6 );
176         } else {
177                 /* don't boot from this device (could be a scanner :-)) */
178                 return -1;
179         }
180
181         /* wait for spin-up (or whatever) to complete */
182         for( i=0; ; i++ ) {
183                 if( i > 300 ) {
184                         printk("SCSI timeout (sense %x)\n", sense );
185                         return -1;
186                 }
187                 sense = scsi_cmd( sd, test_unit_ready_cmd, 6 );
188                 if( (sense & 0xf0000) == 0x20000 ) {
189                         OSI_USleep( 10000 );
190                         continue;
191                 }
192                 break;
193         }
194
195         info->valid = 1;
196         return 0;
197 }
198
199 /* ( -- success? ) */
200 static void
201 scsi_open( instance_data_t *sd )
202 {
203         static int once = 0;
204         phandle_t ph;
205
206         fword("my-unit");
207         sd->target = POP();
208
209         if( !once ) {
210                 once++;
211                 OSI_SCSIControl( SCSI_CTRL_INIT, 0 );
212         }
213
214         /* obtiain device information */
215         if( inquiry(sd) )
216                 RET(0);
217
218         selfword("open-deblocker");
219
220         /* interpose disk-label */
221         ph = find_dev("/packages/disk-label");
222         fword("my-args");
223         PUSH_ph( ph );
224         fword("interpose");
225
226         PUSH( -1 );
227 }
228
229 /* ( -- ) */
230 static void
231 scsi_close( instance_data_t *pb )
232 {
233         selfword("close-deblocker");
234 }
235
236
237 /* ( -- bs ) */
238 static void
239 scsi_block_size( instance_data_t *sd )
240 {
241         PUSH( sd->info->blocksize );
242 }
243
244 /* ( -- maxbytes ) */
245 static void
246 scsi_max_transfer( instance_data_t *sd )
247 {
248         PUSH( 1024*1024 );
249 }
250
251 static void
252 scsi_initialize( instance_data_t *sd )
253 {
254         fword("is-deblocker");
255 }
256
257
258 NODE_METHODS( scsi ) = {
259         { NULL,                 scsi_initialize },
260         { "open",               scsi_open               },
261         { "close",              scsi_close              },
262         { "read-blocks",        scsi_read_blocks        },
263         { "block-size",         scsi_block_size },
264         { "max-transfer",       scsi_max_transfer       },
265 };
266
267 void
268 osiscsi_init( void )
269 {
270         REGISTER_NODE( scsi );
271 }