1 /*  cdrdao - write audio CD-Rs in disc-at-once mode
2  *
3  *  Copyright (C) 1998-2001  Andreas Mueller <andreas@daneb.de>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 /* SCSI interface implemenation for FreeBSD.
21  * Written by Max Khon <fjoe@iclub.nsu.ru>
22  */
23 
24 #include <config.h>
25 
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <string.h>
29 
30 #include <camlib.h>
31 #include <bus/cam/scsi/scsi_message.h>
32 
33 #include "ScsiIf.h"
34 #include "log.h"
35 #include "util.h"
36 
37 #define DEF_RETRY_COUNT 1
38 
39 #include "decodeSense.cc"
40 
41 class ScsiIfImpl {
42 public:
43 	char *			devname;
44 	struct cam_device *	dev;
45 	union ccb *		ccb;
46 	int			timeout;	/* timeout in ms */
47 };
48 
ScsiIf(const char * devname)49 ScsiIf::ScsiIf(const char *devname)
50 {
51 	impl_ = new ScsiIfImpl;
52 	impl_->devname = strdupCC(devname);
53 	impl_->dev = NULL;
54 	impl_->ccb = NULL;
55 	impl_->timeout = 5000;
56 
57 	maxDataLen_ = 32 * 1024;
58 	vendor_[0] = 0;
59 	product_[0] = 0;
60 	revision_[0] = 0;
61 }
62 
~ScsiIf()63 ScsiIf::~ScsiIf()
64 {
65 	if (impl_->ccb)
66 		cam_freeccb(impl_->ccb);
67 	if (impl_->dev)
68 		cam_close_device(impl_->dev);
69 	delete[] impl_->devname;
70 	delete impl_;
71 }
72 
73 // opens scsi device
74 // return: 0: OK
75 //         1: device could not be opened
76 //         2: inquiry failed
init()77 int ScsiIf::init()
78 {
79 	if ((impl_->dev = cam_open_device(impl_->devname, O_RDWR)) == NULL) {
80 		log_message(-2, "%s", cam_errbuf);
81 		return 1;
82 	}
83 
84 	impl_->ccb = cam_getccb(impl_->dev);
85 	if (impl_->ccb == NULL) {
86 		log_message(-2, "init: error allocating ccb");
87 		return 1;
88 	}
89 
90 	if (inquiry())
91 		return 2;
92 
93 	return 0;
94 }
95 
96 // Sets given timeout value in seconds and returns old timeout.
97 // return: old timeout
timeout(int t)98 int ScsiIf::timeout(int t)
99 {
100 	int old = impl_->timeout;
101 	impl_->timeout = t*1000;
102 	return old/1000;
103 }
104 
105 // sends a scsi command and receives data
106 // return 0: OK
107 //        1: scsi command failed (os level, no sense data available)
108 //        2: scsi command failed (sense data available)
sendCmd(const unsigned char * cmd,int cmdLen,const unsigned char * dataOut,int dataOutLen,unsigned char * dataIn,int dataInLen,int showMessage)109 int ScsiIf::sendCmd(const unsigned char *cmd, int cmdLen,
110 		    const unsigned char *dataOut, int dataOutLen,
111 		    unsigned char *dataIn, int dataInLen,
112 		    int showMessage)
113 {
114 	int		retval;
115 	int		flags = CAM_DIR_NONE;
116 	u_int8_t *	data_ptr;
117 	size_t		data_len;
118 
119 	bzero(impl_->ccb, sizeof(union ccb));
120 	bcopy(cmd, &impl_->ccb->csio.cdb_io.cdb_bytes, cmdLen);
121 
122 	if (dataOut && dataOutLen > 0) {
123 		data_ptr = (u_int8_t*) dataOut;
124 		data_len = dataOutLen;
125 		flags = CAM_DIR_OUT;
126 	}
127 	else if (dataIn && dataInLen > 0) {
128 		data_ptr = dataIn;
129 		data_len = dataInLen;
130 		flags = CAM_DIR_IN;
131 	}
132 
133 	cam_fill_csio(&impl_->ccb->csio,
134 		      DEF_RETRY_COUNT,
135 		      NULL,
136 		      flags | CAM_DEV_QFRZDIS,
137 		      MSG_SIMPLE_Q_TAG,
138 		      data_ptr,
139 		      data_len,
140 		      SSD_FULL_SIZE,
141 		      cmdLen,
142 		      impl_->timeout);
143 	if ((retval = cam_send_ccb(impl_->dev, impl_->ccb)) < 0
144 	||  (impl_->ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
145 		if (retval < 0) {
146 			log_message(-2, "sendCmd: error sending command");
147 			return 1;
148 		}
149 
150 		if ((impl_->ccb->ccb_h.status & CAM_STATUS_MASK) ==
151 		    CAM_SCSI_STATUS_ERROR) {
152                         if (showMessage)
153 			        printError();
154 
155 			return 2;
156 		}
157 		return 1;
158 	}
159 
160 	return 0;
161 }
162 
getSense(int & len) const163 const unsigned char *ScsiIf::getSense(int &len) const
164 {
165 	len = impl_->ccb->csio.sense_len;
166 	return (const unsigned char*) &impl_->ccb->csio.sense_data;
167 }
168 
printError()169 void ScsiIf::printError()
170 {
171         decodeSense((const unsigned char*) &impl_->ccb->csio.sense_data,
172 		    impl_->ccb->csio.sense_len);
173 }
174 
inquiry()175 int ScsiIf::inquiry()
176 {
177 	int i;
178 	struct scsi_inquiry_data inq_data;
179 
180 	bzero(impl_->ccb, sizeof(union ccb));
181 	bzero(&inq_data, sizeof(inq_data));
182 
183 	scsi_inquiry(&impl_->ccb->csio,
184 		     DEF_RETRY_COUNT,
185 		     NULL,
186 		     MSG_SIMPLE_Q_TAG,
187 		     (u_int8_t*) &inq_data,
188 		     sizeof(inq_data),
189 		     0,
190 		     0,
191 		     SSD_FULL_SIZE,
192 		     impl_->timeout);
193 	impl_->ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
194 
195 	if (cam_send_ccb(impl_->dev, impl_->ccb) < 0) {
196 		if ((impl_->ccb->ccb_h.status & CAM_STATUS_MASK) !=
197 		    CAM_SCSI_STATUS_ERROR) {
198 			log_message(-2, "%s", cam_errbuf);
199 			return 1;
200 		}
201 
202 		printError();
203 		return 1;
204 	}
205 
206 	strncpy(vendor_, inq_data.vendor, 8);
207 	vendor_[8] = 0;
208 
209 	strncpy(product_, inq_data.product, 16);
210 	product_[16] = 0;
211 
212 	strncpy(revision_, inq_data.revision, 4);
213 	revision_[4] = 0;
214 
215 	for (i = 7; i >= 0 && vendor_[i] == ' '; i--)
216 		vendor_[i] = 0;
217 
218 	for (i = 15; i >= 0 && product_[i] == ' '; i--)
219 		product_[i] = 0;
220 
221 	for (i = 3; i >= 0 && revision_[i] == ' '; i--)
222 		revision_[i] = 0;
223 
224 	return 0;
225 }
226 
scan(int * len,char * scsi_dev_path)227 ScsiIf::ScanData *ScsiIf::scan(int *len, char* scsi_dev_path)
228 {
229   *len = 0;
230   return NULL;
231 }
232 
233 #include "ScsiIf-common.cc"
234