diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/scsicam.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/scsicam.c')
-rw-r--r-- | drivers/scsi/scsicam.c | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/drivers/scsi/scsicam.c b/drivers/scsi/scsicam.c new file mode 100644 index 000000000000..b78354fc4b17 --- /dev/null +++ b/drivers/scsi/scsicam.c | |||
@@ -0,0 +1,245 @@ | |||
1 | /* | ||
2 | * scsicam.c - SCSI CAM support functions, use for HDIO_GETGEO, etc. | ||
3 | * | ||
4 | * Copyright 1993, 1994 Drew Eckhardt | ||
5 | * Visionary Computing | ||
6 | * (Unix and Linux consulting and custom programming) | ||
7 | * drew@Colorado.EDU | ||
8 | * +1 (303) 786-7975 | ||
9 | * | ||
10 | * For more information, please consult the SCSI-CAM draft. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/fs.h> | ||
15 | #include <linux/genhd.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/blkdev.h> | ||
18 | #include <linux/buffer_head.h> | ||
19 | #include <asm/unaligned.h> | ||
20 | |||
21 | #include <scsi/scsicam.h> | ||
22 | |||
23 | |||
24 | static int setsize(unsigned long capacity, unsigned int *cyls, unsigned int *hds, | ||
25 | unsigned int *secs); | ||
26 | |||
27 | unsigned char *scsi_bios_ptable(struct block_device *dev) | ||
28 | { | ||
29 | unsigned char *res = kmalloc(66, GFP_KERNEL); | ||
30 | if (res) { | ||
31 | struct block_device *bdev = dev->bd_contains; | ||
32 | Sector sect; | ||
33 | void *data = read_dev_sector(bdev, 0, §); | ||
34 | if (data) { | ||
35 | memcpy(res, data + 0x1be, 66); | ||
36 | put_dev_sector(sect); | ||
37 | } else { | ||
38 | kfree(res); | ||
39 | res = NULL; | ||
40 | } | ||
41 | } | ||
42 | return res; | ||
43 | } | ||
44 | EXPORT_SYMBOL(scsi_bios_ptable); | ||
45 | |||
46 | /* | ||
47 | * Function : int scsicam_bios_param (struct block_device *bdev, ector_t capacity, int *ip) | ||
48 | * | ||
49 | * Purpose : to determine the BIOS mapping used for a drive in a | ||
50 | * SCSI-CAM system, storing the results in ip as required | ||
51 | * by the HDIO_GETGEO ioctl(). | ||
52 | * | ||
53 | * Returns : -1 on failure, 0 on success. | ||
54 | * | ||
55 | */ | ||
56 | |||
57 | int scsicam_bios_param(struct block_device *bdev, sector_t capacity, int *ip) | ||
58 | { | ||
59 | unsigned char *p; | ||
60 | int ret; | ||
61 | |||
62 | p = scsi_bios_ptable(bdev); | ||
63 | if (!p) | ||
64 | return -1; | ||
65 | |||
66 | /* try to infer mapping from partition table */ | ||
67 | ret = scsi_partsize(p, (unsigned long)capacity, (unsigned int *)ip + 2, | ||
68 | (unsigned int *)ip + 0, (unsigned int *)ip + 1); | ||
69 | kfree(p); | ||
70 | |||
71 | if (ret == -1) { | ||
72 | /* pick some standard mapping with at most 1024 cylinders, | ||
73 | and at most 62 sectors per track - this works up to | ||
74 | 7905 MB */ | ||
75 | ret = setsize((unsigned long)capacity, (unsigned int *)ip + 2, | ||
76 | (unsigned int *)ip + 0, (unsigned int *)ip + 1); | ||
77 | } | ||
78 | |||
79 | /* if something went wrong, then apparently we have to return | ||
80 | a geometry with more than 1024 cylinders */ | ||
81 | if (ret || ip[0] > 255 || ip[1] > 63) { | ||
82 | if ((capacity >> 11) > 65534) { | ||
83 | ip[0] = 255; | ||
84 | ip[1] = 63; | ||
85 | } else { | ||
86 | ip[0] = 64; | ||
87 | ip[1] = 32; | ||
88 | } | ||
89 | |||
90 | if (capacity > 65535*63*255) | ||
91 | ip[2] = 65535; | ||
92 | else | ||
93 | ip[2] = (unsigned long)capacity / (ip[0] * ip[1]); | ||
94 | } | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | EXPORT_SYMBOL(scsicam_bios_param); | ||
99 | |||
100 | /* | ||
101 | * Function : static int scsi_partsize(unsigned char *buf, unsigned long | ||
102 | * capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs); | ||
103 | * | ||
104 | * Purpose : to determine the BIOS mapping used to create the partition | ||
105 | * table, storing the results in *cyls, *hds, and *secs | ||
106 | * | ||
107 | * Returns : -1 on failure, 0 on success. | ||
108 | * | ||
109 | */ | ||
110 | |||
111 | int scsi_partsize(unsigned char *buf, unsigned long capacity, | ||
112 | unsigned int *cyls, unsigned int *hds, unsigned int *secs) | ||
113 | { | ||
114 | struct partition *p = (struct partition *)buf, *largest = NULL; | ||
115 | int i, largest_cyl; | ||
116 | int cyl, ext_cyl, end_head, end_cyl, end_sector; | ||
117 | unsigned int logical_end, physical_end, ext_physical_end; | ||
118 | |||
119 | |||
120 | if (*(unsigned short *) (buf + 64) == 0xAA55) { | ||
121 | for (largest_cyl = -1, i = 0; i < 4; ++i, ++p) { | ||
122 | if (!p->sys_ind) | ||
123 | continue; | ||
124 | #ifdef DEBUG | ||
125 | printk("scsicam_bios_param : partition %d has system \n", | ||
126 | i); | ||
127 | #endif | ||
128 | cyl = p->cyl + ((p->sector & 0xc0) << 2); | ||
129 | if (cyl > largest_cyl) { | ||
130 | largest_cyl = cyl; | ||
131 | largest = p; | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | if (largest) { | ||
136 | end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2); | ||
137 | end_head = largest->end_head; | ||
138 | end_sector = largest->end_sector & 0x3f; | ||
139 | |||
140 | if (end_head + 1 == 0 || end_sector == 0) | ||
141 | return -1; | ||
142 | |||
143 | #ifdef DEBUG | ||
144 | printk("scsicam_bios_param : end at h = %d, c = %d, s = %d\n", | ||
145 | end_head, end_cyl, end_sector); | ||
146 | #endif | ||
147 | |||
148 | physical_end = end_cyl * (end_head + 1) * end_sector + | ||
149 | end_head * end_sector + end_sector; | ||
150 | |||
151 | /* This is the actual _sector_ number at the end */ | ||
152 | logical_end = get_unaligned(&largest->start_sect) | ||
153 | + get_unaligned(&largest->nr_sects); | ||
154 | |||
155 | /* This is for >1023 cylinders */ | ||
156 | ext_cyl = (logical_end - (end_head * end_sector + end_sector)) | ||
157 | / (end_head + 1) / end_sector; | ||
158 | ext_physical_end = ext_cyl * (end_head + 1) * end_sector + | ||
159 | end_head * end_sector + end_sector; | ||
160 | |||
161 | #ifdef DEBUG | ||
162 | printk("scsicam_bios_param : logical_end=%d physical_end=%d ext_physical_end=%d ext_cyl=%d\n" | ||
163 | ,logical_end, physical_end, ext_physical_end, ext_cyl); | ||
164 | #endif | ||
165 | |||
166 | if ((logical_end == physical_end) || | ||
167 | (end_cyl == 1023 && ext_physical_end == logical_end)) { | ||
168 | *secs = end_sector; | ||
169 | *hds = end_head + 1; | ||
170 | *cyls = capacity / ((end_head + 1) * end_sector); | ||
171 | return 0; | ||
172 | } | ||
173 | #ifdef DEBUG | ||
174 | printk("scsicam_bios_param : logical (%u) != physical (%u)\n", | ||
175 | logical_end, physical_end); | ||
176 | #endif | ||
177 | } | ||
178 | return -1; | ||
179 | } | ||
180 | EXPORT_SYMBOL(scsi_partsize); | ||
181 | |||
182 | /* | ||
183 | * Function : static int setsize(unsigned long capacity,unsigned int *cyls, | ||
184 | * unsigned int *hds, unsigned int *secs); | ||
185 | * | ||
186 | * Purpose : to determine a near-optimal int 0x13 mapping for a | ||
187 | * SCSI disk in terms of lost space of size capacity, storing | ||
188 | * the results in *cyls, *hds, and *secs. | ||
189 | * | ||
190 | * Returns : -1 on failure, 0 on success. | ||
191 | * | ||
192 | * Extracted from | ||
193 | * | ||
194 | * WORKING X3T9.2 | ||
195 | * DRAFT 792D | ||
196 | * | ||
197 | * | ||
198 | * Revision 6 | ||
199 | * 10-MAR-94 | ||
200 | * Information technology - | ||
201 | * SCSI-2 Common access method | ||
202 | * transport and SCSI interface module | ||
203 | * | ||
204 | * ANNEX A : | ||
205 | * | ||
206 | * setsize() converts a read capacity value to int 13h | ||
207 | * head-cylinder-sector requirements. It minimizes the value for | ||
208 | * number of heads and maximizes the number of cylinders. This | ||
209 | * will support rather large disks before the number of heads | ||
210 | * will not fit in 4 bits (or 6 bits). This algorithm also | ||
211 | * minimizes the number of sectors that will be unused at the end | ||
212 | * of the disk while allowing for very large disks to be | ||
213 | * accommodated. This algorithm does not use physical geometry. | ||
214 | */ | ||
215 | |||
216 | static int setsize(unsigned long capacity, unsigned int *cyls, unsigned int *hds, | ||
217 | unsigned int *secs) | ||
218 | { | ||
219 | unsigned int rv = 0; | ||
220 | unsigned long heads, sectors, cylinders, temp; | ||
221 | |||
222 | cylinders = 1024L; /* Set number of cylinders to max */ | ||
223 | sectors = 62L; /* Maximize sectors per track */ | ||
224 | |||
225 | temp = cylinders * sectors; /* Compute divisor for heads */ | ||
226 | heads = capacity / temp; /* Compute value for number of heads */ | ||
227 | if (capacity % temp) { /* If no remainder, done! */ | ||
228 | heads++; /* Else, increment number of heads */ | ||
229 | temp = cylinders * heads; /* Compute divisor for sectors */ | ||
230 | sectors = capacity / temp; /* Compute value for sectors per | ||
231 | track */ | ||
232 | if (capacity % temp) { /* If no remainder, done! */ | ||
233 | sectors++; /* Else, increment number of sectors */ | ||
234 | temp = heads * sectors; /* Compute divisor for cylinders */ | ||
235 | cylinders = capacity / temp; /* Compute number of cylinders */ | ||
236 | } | ||
237 | } | ||
238 | if (cylinders == 0) | ||
239 | rv = (unsigned) -1; /* Give error if 0 cylinders */ | ||
240 | |||
241 | *cyls = (unsigned int) cylinders; /* Stuff return values */ | ||
242 | *secs = (unsigned int) sectors; | ||
243 | *hds = (unsigned int) heads; | ||
244 | return (rv); | ||
245 | } | ||