2009-11-02 Samuel Thibault <samuel.thibault@ens-lyon.org>
[grub2/phcoder/solaris.git] / disk / scsi.c
blobd0e248511a20d30ff45c1964dae2667853975029
1 /* scsi.c - scsi support. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/disk.h>
21 #include <grub/dl.h>
22 #include <grub/kernel.h>
23 #include <grub/misc.h>
24 #include <grub/mm.h>
25 #include <grub/types.h>
26 #include <grub/scsi.h>
27 #include <grub/scsicmd.h>
30 static grub_scsi_dev_t grub_scsi_dev_list;
32 void
33 grub_scsi_dev_register (grub_scsi_dev_t dev)
35 dev->next = grub_scsi_dev_list;
36 grub_scsi_dev_list = dev;
39 void
40 grub_scsi_dev_unregister (grub_scsi_dev_t dev)
42 grub_scsi_dev_t *p, q;
44 for (p = &grub_scsi_dev_list, q = *p; q; p = &(q->next), q = q->next)
45 if (q == dev)
47 *p = q->next;
48 break;
53 /* Determine the the device is removable and the type of the device
54 SCSI. */
55 static grub_err_t
56 grub_scsi_inquiry (grub_scsi_t scsi)
58 struct grub_scsi_inquiry iq;
59 struct grub_scsi_inquiry_data iqd;
60 grub_err_t err;
62 iq.opcode = grub_scsi_cmd_inquiry;
63 iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
64 iq.reserved = 0;
65 iq.alloc_length = 0x24; /* XXX: Hardcoded for now */
66 iq.reserved2 = 0;
68 err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq,
69 sizeof (iqd), (char *) &iqd);
70 if (err)
71 return err;
73 scsi->devtype = iqd.devtype & GRUB_SCSI_DEVTYPE_MASK;
74 scsi->removable = iqd.rmb >> GRUB_SCSI_REMOVABLE_BIT;
76 return GRUB_ERR_NONE;
79 /* Read the capacity and block size of SCSI. */
80 static grub_err_t
81 grub_scsi_read_capacity (grub_scsi_t scsi)
83 struct grub_scsi_read_capacity rc;
84 struct grub_scsi_read_capacity_data rcd;
85 grub_err_t err;
87 rc.opcode = grub_scsi_cmd_read_capacity;
88 rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
89 grub_memset (rc.reserved, 0, sizeof (rc.reserved));
91 err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc,
92 sizeof (rcd), (char *) &rcd);
93 if (err)
94 return err;
96 scsi->size = grub_be_to_cpu32 (rcd.size);
97 scsi->blocksize = grub_be_to_cpu32 (rcd.blocksize);
99 return GRUB_ERR_NONE;
102 /* Send a SCSI request for DISK: read SIZE sectors starting with
103 sector SECTOR to BUF. */
104 static grub_err_t
105 grub_scsi_read10 (grub_disk_t disk, grub_disk_addr_t sector,
106 grub_size_t size, char *buf)
108 grub_scsi_t scsi;
109 struct grub_scsi_read10 rd;
111 scsi = disk->data;
113 rd.opcode = grub_scsi_cmd_read10;
114 rd.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
115 rd.lba = grub_cpu_to_be32 (sector);
116 rd.reserved = 0;
117 rd.size = grub_cpu_to_be16 (size);
118 rd.reserved2 = 0;
119 rd.pad = 0;
121 return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
124 /* Send a SCSI request for DISK: read SIZE sectors starting with
125 sector SECTOR to BUF. */
126 static grub_err_t
127 grub_scsi_read12 (grub_disk_t disk, grub_disk_addr_t sector,
128 grub_size_t size, char *buf)
130 grub_scsi_t scsi;
131 struct grub_scsi_read12 rd;
133 scsi = disk->data;
135 rd.opcode = grub_scsi_cmd_read12;
136 rd.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
137 rd.lba = grub_cpu_to_be32 (sector);
138 rd.size = grub_cpu_to_be32 (size);
139 rd.reserved = 0;
140 rd.control = 0;
142 return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
145 #if 0
146 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
147 sectors starting with SECTOR. */
148 static grub_err_t
149 grub_scsi_write10 (grub_disk_t disk, grub_disk_addr_t sector,
150 grub_size_t size, char *buf)
152 grub_scsi_t scsi;
153 struct grub_scsi_write10 wr;
155 scsi = disk->data;
157 wr.opcode = grub_scsi_cmd_write10;
158 wr.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
159 wr.lba = grub_cpu_to_be32 (sector);
160 wr.reserved = 0;
161 wr.size = grub_cpu_to_be16 (size);
162 wr.reserved2 = 0;
163 wr.pad = 0;
165 return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
168 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
169 sectors starting with SECTOR. */
170 static grub_err_t
171 grub_scsi_write12 (grub_disk_t disk, grub_disk_addr_t sector,
172 grub_size_t size, char *buf)
174 grub_scsi_t scsi;
175 struct grub_scsi_write10 wr;
177 scsi = disk->data;
179 wr.opcode = grub_scsi_cmd_write12;
180 wr.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
181 wr.lba = grub_cpu_to_be32 (sector);
182 wr.size = grub_cpu_to_be32 (size);
183 wr.reserved = 0;
184 wr.pad = 0;
186 return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
188 #endif
191 static int
192 grub_scsi_iterate (int (*hook) (const char *name))
194 grub_scsi_dev_t p;
196 auto int scsi_iterate (const char *name, int luns);
198 int scsi_iterate (const char *name, int luns)
200 char sname[40];
201 int i;
203 /* In case of a single LUN, just return `usbX'. */
204 if (luns == 1)
205 return hook (name);
207 /* In case of multiple LUNs, every LUN will get a prefix to
208 distinguish it. */
209 for (i = 0; i < luns; i++)
211 grub_sprintf (sname, "%s%c", name, 'a' + i);
212 if (hook (sname))
213 return 1;
215 return 0;
218 for (p = grub_scsi_dev_list; p; p = p->next)
219 if (p->iterate && (p->iterate) (scsi_iterate))
220 return 1;
222 return 0;
225 static grub_err_t
226 grub_scsi_open (const char *name, grub_disk_t disk)
228 grub_scsi_dev_t p;
229 grub_scsi_t scsi;
230 grub_err_t err;
231 int len;
232 int lun;
234 scsi = grub_malloc (sizeof (*scsi));
235 if (! scsi)
236 return grub_errno;
238 len = grub_strlen (name);
239 lun = name[len - 1] - 'a';
241 /* Try to detect a LUN ('a'-'z'), otherwise just use the first
242 LUN. */
243 if (lun < 0 || lun > 26)
244 lun = 0;
246 for (p = grub_scsi_dev_list; p; p = p->next)
248 if (p->open (name, scsi))
249 continue;
251 disk->id = (unsigned long) "scsi"; /* XXX */
252 disk->data = scsi;
253 scsi->dev = p;
254 scsi->lun = lun;
255 scsi->name = grub_strdup (name);
256 if (! scsi->name)
258 grub_free (scsi);
259 return grub_errno;
262 grub_dprintf ("scsi", "dev opened\n");
264 err = grub_scsi_inquiry (scsi);
265 if (err)
267 grub_free (scsi);
268 grub_dprintf ("scsi", "inquiry failed\n");
269 return err;
272 grub_dprintf ("scsi", "inquiry: devtype=0x%02x removable=%d\n",
273 scsi->devtype, scsi->removable);
275 /* Try to be conservative about the device types
276 supported. */
277 if (scsi->devtype != grub_scsi_devtype_direct
278 && scsi->devtype != grub_scsi_devtype_cdrom)
280 grub_free (scsi);
281 return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
282 "unknown SCSI device");
285 if (scsi->devtype == grub_scsi_devtype_cdrom)
286 disk->has_partitions = 0;
287 else
288 disk->has_partitions = 1;
290 err = grub_scsi_read_capacity (scsi);
291 if (err)
293 grub_free (scsi);
294 grub_dprintf ("scsi", "READ CAPACITY failed\n");
295 return err;
298 /* SCSI blocks can be something else than 512, although GRUB
299 wants 512 byte blocks. */
300 disk->total_sectors = ((scsi->size * scsi->blocksize)
301 << GRUB_DISK_SECTOR_BITS);
303 grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n",
304 (unsigned long long) disk->total_sectors,
305 scsi->blocksize);
307 return GRUB_ERR_NONE;
310 grub_free (scsi);
312 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk");
315 static void
316 grub_scsi_close (grub_disk_t disk)
318 grub_scsi_t scsi;
320 scsi = disk->data;
321 scsi->dev->close (scsi);
322 grub_free (scsi);
325 static grub_err_t
326 grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector,
327 grub_size_t size, char *buf)
329 grub_scsi_t scsi;
331 scsi = disk->data;
333 /* SCSI sectors are variable in size. GRUB uses 512 byte
334 sectors. */
335 if (scsi->blocksize != GRUB_DISK_SECTOR_SIZE)
337 unsigned spb = scsi->blocksize >> GRUB_DISK_SECTOR_BITS;
338 if (! (spb != 0 && (scsi->blocksize & GRUB_DISK_SECTOR_SIZE) == 0))
339 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
340 "Unsupported SCSI block size");
342 grub_uint32_t sector_mod = 0;
343 sector = grub_divmod64 (sector, spb, &sector_mod);
345 if (! (sector_mod == 0 && size % spb == 0))
346 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
347 "Unaligned SCSI read not supported");
349 size /= spb;
352 /* Depending on the type, select a read function. */
353 switch (scsi->devtype)
355 case grub_scsi_devtype_direct:
356 return grub_scsi_read10 (disk, sector, size, buf);
358 case grub_scsi_devtype_cdrom:
359 return grub_scsi_read12 (disk, sector, size, buf);
362 /* XXX: Never reached. */
363 return GRUB_ERR_NONE;
366 static grub_err_t
367 grub_scsi_write (grub_disk_t disk __attribute((unused)),
368 grub_disk_addr_t sector __attribute((unused)),
369 grub_size_t size __attribute((unused)),
370 const char *buf __attribute((unused)))
372 #if 0
373 /* XXX: Not tested yet! */
375 /* XXX: This should depend on the device type? */
376 return grub_scsi_write10 (disk, sector, size, buf);
377 #endif
378 return GRUB_ERR_NOT_IMPLEMENTED_YET;
382 static struct grub_disk_dev grub_scsi_dev =
384 .name = "scsi",
385 .id = GRUB_DISK_DEVICE_SCSI_ID,
386 .iterate = grub_scsi_iterate,
387 .open = grub_scsi_open,
388 .close = grub_scsi_close,
389 .read = grub_scsi_read,
390 .write = grub_scsi_write,
391 .next = 0
394 GRUB_MOD_INIT(scsi)
396 grub_disk_dev_register (&grub_scsi_dev);
399 GRUB_MOD_FINI(scsi)
401 grub_disk_dev_unregister (&grub_scsi_dev);