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