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 #ifdef MODULE
14 /*
15 * Don't import our own symbols, as this would severely mess up our
16 * symbol tables.
17 */
18 #define _SCSI_SYMS_VER_
19 #include <linux/autoconf.h>
20 #include <linux/module.h>
21 #include <linux/version.h>
22 #else
23 #define MOD_INC_USE_COUNT
24 #define MOD_DEC_USE_COUNT
25 #endif
26
27 #include <linux/fs.h>
28 #include <linux/genhd.h>
29 #include <linux/kernel.h>
30 #include "../block/blk.h"
31 #include "scsi.h"
32 #include "hosts.h"
33 #include "sd.h"
34
35 static int partsize(struct buffer_head *bh, unsigned long capacity,
36 unsigned int *cyls, unsigned int *hds, unsigned int *secs);
37 static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
38 unsigned int *secs);
39
40 /*
41 * Function : int scsicam_bios_param (Disk *disk, int dev, int *ip)
42 *
43 * Purpose : to determine the BIOS mapping used for a drive in a
44 * SCSI-CAM system, storing the results in ip as required
45 * by the HDIO_GETGEO ioctl().
46 *
47 * Returns : -1 on failure, 0 on success.
48 *
49 */
50
51 int scsicam_bios_param (Disk *disk, /* SCSI disk */
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
52 int dev, /* Device major, minor */
53 int *ip /* Heads, sectors, cylinders in that order */) {
54 struct buffer_head *bh;
55 int ret_code;
56 int size = disk->capacity;
57
58 if (!(bh = bread(dev & ~0xf,0,1024)))
59 return -1;
60
61 #ifdef DEBUG
62 printk ("scsicam_bios_param : trying existing mapping\n");
63 #endif
64 ret_code = partsize (bh, (unsigned long) size, (unsigned int *) ip + 2,
65 (unsigned int *) ip + 0, (unsigned int *) ip + 1);
66 brelse (bh);
67
68 if (ret_code == -1) {
69 #ifdef DEBUG
70 printk ("scsicam_bios_param : trying optimal mapping\n");
71 #endif
72 ret_code = setsize ((unsigned long) size, (unsigned int *) ip + 2,
73 (unsigned int *) ip + 0, (unsigned int *) ip + 1);
74 }
75
76 return ret_code;
77 }
78
79 /*
80 * Function : static int partsize(struct buffer_head *bh, unsigned long
81 * capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs);
82 *
83 * Purpose : to determine the BIOS mapping used to create the partition
84 * table, storing the results in *cyls, *hds, and *secs
85 *
86 * Returns : -1 on failure, 0 on success.
87 *
88 */
89
90 static int partsize(struct buffer_head *bh, unsigned long capacity,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
91 unsigned int *cyls, unsigned int *hds, unsigned int *secs) {
92 struct partition *p, *largest = NULL;
93 int i, largest_cyl;
94 int cyl, end_head, end_cyl, end_sector;
95 unsigned int logical_end, physical_end;
96
97
98 if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
99 for (largest_cyl = -1, p = (struct partition *)
100 (0x1BE + bh->b_data), i = 0; i < 4; ++i, ++p) {
101 if (!p->sys_ind)
102 continue;
103 #ifdef DEBUG
104 printk ("scsicam_bios_param : partition %d has system \n",
105 i);
106 #endif
107 cyl = p->cyl + ((p->sector & 0xc0) << 2);
108 if (cyl > largest_cyl) {
109 largest_cyl = cyl;
110 largest = p;
111 }
112 }
113 }
114
115 if (largest) {
116 end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
117 end_head = largest->end_head;
118 end_sector = largest->end_sector & 0x3f;
119 #ifdef DEBUG
120 printk ("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
121 end_head, end_cyl, end_sector);
122 #endif
123
124 physical_end = end_cyl * (end_head + 1) * end_sector +
125 end_head * end_sector + end_sector;
126
127 /* This is the actual _sector_ number at the end */
128 logical_end = largest->start_sect + largest->nr_sects;
129
130 if (logical_end == physical_end) {
131 *secs = end_sector;
132 *hds = end_head + 1;
133 *cyls = capacity / ((end_head + 1) * end_sector);
134 return 0;
135 }
136 #ifdef DEBUG
137 printk ("scsicam_bios_param : logical (%u) != physical (%u)\n",
138 logical_end, physical_end);
139 #endif
140 }
141 return -1;
142 }
143
144 /*
145 * Function : static int setsize(unsigned long capacity,unsigned int *cyls,
146 * unsigned int *hds, unsigned int *secs);
147 *
148 * Purpose : to determine a near-optimal int 0x13 mapping for a
149 * SCSI disk in terms of lost space of size capacity, storing
150 * the results in *cyls, *hds, and *secs.
151 *
152 * Returns : -1 on failure, 0 on success.
153 *
154 * Extracted from
155 *
156 * WORKING X3T9.2
157 * DRAFT 792D
158 *
159 *
160 * Revision 6
161 * 10-MAR-94
162 * Information technology -
163 * SCSI-2 Common access method
164 * transport and SCSI interface module
165 *
166 * ANNEX A :
167 *
168 * setsize() converts a read capacity value to int 13h
169 * head-cylinder-sector requirements. It minimizes the value for
170 * number of heads and maximizes the number of cylinders. This
171 * will support rather large disks before the number of heads
172 * will not fit in 4 bits (or 6 bits). This algorithm also
173 * minimizes the number of sectors that will be unused at the end
174 * of the disk while allowing for very large disks to be
175 * accommodated. This algorithm does not use physical geometry.
176 */
177
178 static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
179 unsigned int *secs) {
180 unsigned int rv = 0;
181 unsigned long heads, sectors, cylinders, temp;
182
183 cylinders = 1024L; /* Set number of cylinders to max */
184 sectors = 62L; /* Maximize sectors per track */
185
186 temp = cylinders * sectors; /* Compute divisor for heads */
187 heads = capacity / temp; /* Compute value for number of heads */
188 if (capacity % temp) { /* If no remainder, done! */
189 heads++; /* Else, increment number of heads */
190 temp = cylinders * heads; /* Compute divisor for sectors */
191 sectors = capacity / temp; /* Compute value for sectors per
192 track */
193 if (capacity % temp) { /* If no remainder, done! */
194 sectors++; /* Else, increment number of sectors */
195 temp = heads * sectors; /* Compute divisor for cylinders */
196 cylinders = capacity / temp;/* Compute number of cylinders */
197 }
198 }
199 if (cylinders == 0) rv=(unsigned)-1;/* Give error if 0 cylinders */
200
201 *cyls = (unsigned int) cylinders; /* Stuff return values */
202 *secs = (unsigned int) sectors;
203 *hds = (unsigned int) heads;
204 return(rv);
205 }