ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/Linux/scsi_linux.cpp
Revision: 1.9
Committed: 2008-01-01T09:40:33Z (16 years, 6 months ago) by gbeauche
Branch: MAIN
CVS Tags: HEAD
Changes since 1.8: +1 -1 lines
Log Message:
Happy New Year!

File Contents

# Content
1 /*
2 * scsi_linux.cpp - SCSI Manager, Linux specific stuff
3 *
4 * Basilisk II (C) 1997-2008 Christian Bauer
5 *
6 * This program 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 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22
23 #include <sys/ioctl.h>
24 #include <linux/param.h>
25 #include <linux/../scsi/sg.h> // workaround for broken RedHat 6.0 /usr/include/scsi
26 #include <unistd.h>
27 #include <errno.h>
28
29 #define DRIVER_SENSE 0x08
30
31 #include "main.h"
32 #include "prefs.h"
33 #include "user_strings.h"
34 #include "scsi.h"
35
36 #define DEBUG 0
37 #include "debug.h"
38
39
40 // Global variables
41 static int fds[8]; // fd's for 8 units
42 static int fd; // Active fd (selected target)
43
44 static uint32 buffer_size; // Size of data buffer
45 static uint8 *buffer = NULL; // Pointer to data buffer
46
47 static uint8 the_cmd[12]; // Active SCSI command
48 static int the_cmd_len;
49
50
51 /*
52 * Initialization
53 */
54
55 void SCSIInit(void)
56 {
57 int id;
58
59 // Allocate buffer
60 buffer = (uint8 *)malloc(buffer_size = 0x10000);
61
62 // Open generic SCSI driver for all 8 units
63 for (id=0; id<8; id++) {
64 char prefs_name[16];
65 sprintf(prefs_name, "scsi%d", id);
66 const char *str = PrefsFindString(prefs_name);
67 if (str) {
68 int fd = fds[id] = open(str, O_RDWR | O_EXCL);
69 if (fd > 0) {
70 // Is it really a Generic SCSI device?
71 int timeout = ioctl(fd, SG_GET_TIMEOUT);
72 if (timeout < 0) {
73 // Error with SG_GET_TIMEOUT, doesn't seem to be a Generic SCSI device
74 char msg[256];
75 sprintf(msg, GetString(STR_SCSI_DEVICE_NOT_SCSI_WARN), str);
76 WarningAlert(msg);
77 close(fd);
78 fds[id] = -1;
79 } else {
80 // Flush unwanted garbage from previous use of device
81 uint8 reply[256];
82 int old_fl = fcntl(fd, F_GETFL);
83 fcntl(fd, F_SETFL, old_fl | O_NONBLOCK);
84 while (read(fd, reply, sizeof(reply)) != -1 || errno != EAGAIN) ;
85 fcntl(fd, F_SETFL, old_fl);
86 }
87 } else {
88 char msg[256];
89 sprintf(msg, GetString(STR_SCSI_DEVICE_OPEN_WARN), str, strerror(errno));
90 WarningAlert(msg);
91 }
92 }
93 }
94
95 // Reset SCSI bus
96 SCSIReset();
97 }
98
99
100 /*
101 * Deinitialization
102 */
103
104 void SCSIExit(void)
105 {
106 // Close all devices
107 for (int i=0; i<8; i++) {
108 int fd = fds[i];
109 if (fd > 0)
110 close(fd);
111 }
112
113 // Free buffer
114 if (buffer) {
115 free(buffer);
116 buffer = NULL;
117 }
118 }
119
120
121 /*
122 * Check if requested data size fits into buffer, allocate new buffer if needed
123 */
124
125 static bool try_buffer(uint32 size)
126 {
127 size += sizeof(sg_header) + 12;
128 if (size <= buffer_size)
129 return true;
130
131 uint8 *new_buffer = (uint8 *)malloc(size);
132 if (new_buffer == NULL)
133 return false;
134 free(buffer);
135 buffer = new_buffer;
136 buffer_size = size;
137 return true;
138 }
139
140
141 /*
142 * Set SCSI command to be sent by scsi_send_cmd()
143 */
144
145 void scsi_set_cmd(int cmd_length, uint8 *cmd)
146 {
147 memcpy(the_cmd, cmd, the_cmd_len = cmd_length);
148 }
149
150
151 /*
152 * Check for presence of SCSI target
153 */
154
155 bool scsi_is_target_present(int id)
156 {
157 return fds[id] > 0;
158 }
159
160
161 /*
162 * Set SCSI target (returns false on error)
163 */
164
165 bool scsi_set_target(int id, int lun)
166 {
167 int new_fd = fds[id];
168 if (new_fd < 0)
169 return false;
170 if (new_fd != fd) {
171 // New target, clear autosense data
172 sg_header *h = (sg_header *)buffer;
173 h->driver_status &= ~DRIVER_SENSE;
174 }
175 fd = new_fd;
176 return true;
177 }
178
179
180 /*
181 * Send SCSI command to active target (scsi_set_command() must have been called),
182 * read/write data according to S/G table (returns false on error); timeout is in 1/60 sec
183 */
184
185 bool scsi_send_cmd(size_t data_length, bool reading, int sg_size, uint8 **sg_ptr, uint32 *sg_len, uint16 *stat, uint32 timeout)
186 {
187 static int pack_id = 0;
188
189 // Check if buffer is large enough, allocate new buffer if needed
190 if (!try_buffer(data_length)) {
191 char str[256];
192 sprintf(str, GetString(STR_SCSI_BUFFER_ERR), data_length);
193 ErrorAlert(str);
194 return false;
195 }
196
197 // Process S/G table when writing
198 if (!reading) {
199 D(bug(" writing to buffer\n"));
200 uint8 *buffer_ptr = buffer + sizeof(sg_header) + the_cmd_len;
201 for (int i=0; i<sg_size; i++) {
202 uint32 len = sg_len[i];
203 D(bug(" %d bytes from %08lx\n", len, sg_ptr[i]));
204 memcpy(buffer_ptr, sg_ptr[i], len);
205 buffer_ptr += len;
206 }
207 }
208
209 // Request Sense and autosense data valid?
210 sg_header *h = (sg_header *)buffer;
211 int res;
212 if (reading && the_cmd[0] == 0x03 && (h->target_status & DRIVER_SENSE)) {
213
214 // Yes, fake command
215 D(bug(" autosense\n"));
216 memcpy(buffer + sizeof(sg_header), h->sense_buffer, 16);
217 h->target_status &= ~DRIVER_SENSE;
218 res = 0;
219 *stat = 0;
220
221 } else {
222
223 // No, send regular command
224 if (timeout) {
225 int to = timeout * HZ / 60;
226 ioctl(fd, SG_SET_TIMEOUT, &to);
227 }
228 ioctl(fd, SG_NEXT_CMD_LEN, &the_cmd_len);
229
230 D(bug(" sending command, length %d\n", data_length));
231
232 int request_size, reply_size;
233 if (reading) {
234 h->pack_len = request_size = sizeof(sg_header) + the_cmd_len;
235 h->reply_len = reply_size = sizeof(sg_header) + data_length;
236 } else {
237 h->pack_len = request_size = sizeof(sg_header) + the_cmd_len + data_length;
238 h->reply_len = reply_size = sizeof(sg_header);
239 }
240 h->pack_id = pack_id++;
241 h->result = 0;
242 h->twelve_byte = (the_cmd_len == 12);
243 h->target_status = 0;
244 h->host_status = 0;
245 h->driver_status = 0;
246 h->other_flags = 0;
247 memcpy(buffer + sizeof(sg_header), the_cmd, the_cmd_len);
248
249 res = write(fd, buffer, request_size);
250 D(bug(" request sent, actual %d, result %d\n", res, h->result));
251 if (res >= 0) {
252 res = read(fd, buffer, reply_size);
253 D(bug(" reply read, actual %d, result %d, status %02x\n", res, h->result, h->target_status << 1));
254 }
255
256 *stat = h->target_status << 1;
257 }
258
259 // Process S/G table when reading
260 if (reading && h->result == 0) {
261 D(bug(" reading from buffer\n"));
262 uint8 *buffer_ptr = buffer + sizeof(sg_header);
263 for (int i=0; i<sg_size; i++) {
264 uint32 len = sg_len[i];
265 D(bug(" %d bytes to %08lx\n", len, sg_ptr[i]));
266 memcpy(sg_ptr[i], buffer_ptr, len);
267 buffer_ptr += len;
268 }
269 }
270 return res >= 0;
271 }