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