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, 10 months ago) by gbeauche
Branch: MAIN
CVS Tags: HEAD
Changes since 1.8: +1 -1 lines
Log Message:
Happy New Year!

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * scsi_linux.cpp - SCSI Manager, Linux specific stuff
3     *
4 gbeauche 1.9 * Basilisk II (C) 1997-2008 Christian Bauer
5 cebix 1.1 *
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 cebix 1.6 #define DRIVER_SENSE 0x08
30    
31 cebix 1.1 #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 cebix 1.6 int fd = fds[id] = open(str, O_RDWR | O_EXCL);
69 cebix 1.1 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 cebix 1.3 static bool try_buffer(uint32 size)
126 cebix 1.1 {
127 cebix 1.6 size += sizeof(sg_header) + 12;
128 cebix 1.1 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 cebix 1.6 // New target, clear autosense data
172     sg_header *h = (sg_header *)buffer;
173     h->driver_status &= ~DRIVER_SENSE;
174 cebix 1.1 }
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 cebix 1.6 uint8 *buffer_ptr = buffer + sizeof(sg_header) + the_cmd_len;
201 cebix 1.1 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 cebix 1.6 sg_header *h = (sg_header *)buffer;
211 cebix 1.1 int res;
212 cebix 1.6 if (reading && the_cmd[0] == 0x03 && (h->target_status & DRIVER_SENSE)) {
213 cebix 1.1
214     // Yes, fake command
215     D(bug(" autosense\n"));
216 cebix 1.6 memcpy(buffer + sizeof(sg_header), h->sense_buffer, 16);
217     h->target_status &= ~DRIVER_SENSE;
218 cebix 1.1 res = 0;
219     *stat = 0;
220    
221     } else {
222    
223     // No, send regular command
224 cebix 1.6 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 cebix 1.1 D(bug(" sending command, length %d\n", data_length));
231 cebix 1.6
232 cebix 1.1 int request_size, reply_size;
233     if (reading) {
234 cebix 1.6 h->pack_len = request_size = sizeof(sg_header) + the_cmd_len;
235     h->reply_len = reply_size = sizeof(sg_header) + data_length;
236 cebix 1.1 } else {
237 cebix 1.6 h->pack_len = request_size = sizeof(sg_header) + the_cmd_len + data_length;
238     h->reply_len = reply_size = sizeof(sg_header);
239 cebix 1.1 }
240     h->pack_id = pack_id++;
241     h->result = 0;
242     h->twelve_byte = (the_cmd_len == 12);
243 cebix 1.6 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 cebix 1.1 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 cebix 1.6 D(bug(" reply read, actual %d, result %d, status %02x\n", res, h->result, h->target_status << 1));
254 cebix 1.1 }
255 cebix 1.6
256     *stat = h->target_status << 1;
257 cebix 1.1 }
258    
259     // Process S/G table when reading
260     if (reading && h->result == 0) {
261     D(bug(" reading from buffer\n"));
262 cebix 1.6 uint8 *buffer_ptr = buffer + sizeof(sg_header);
263 cebix 1.1 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     }