configurator: HAVE_SECTION_START_STOP
[ccan] / ccan / iscsi / scsi-lowlevel.c
1 /*
2    Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
3    
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8    
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13    
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17 /*
18  * would be nice if this could grow into a full blown library for scsi to
19  * 1, build a CDB
20  * 2, check how big a complete data-in structure needs to be
21  * 3, unmarshall data-in into a real structure
22  * 4, marshall a real structure into a data-out blob
23  */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stddef.h>
28 #include <string.h>
29 #include <strings.h>
30 #include <stdint.h>
31 #include <arpa/inet.h>
32 #include <ccan/compiler/compiler.h>
33 #include "scsi-lowlevel.h"
34 #include "dlinklist.h"
35
36
37 void scsi_free_scsi_task(struct scsi_task *task)
38 {
39         struct scsi_allocated_memory *mem;
40
41         while((mem = task->mem)) {
42                    DLIST_REMOVE(task->mem, mem);
43                    free(mem);
44         }
45         free(task);
46 }
47
48 static void *scsi_malloc(struct scsi_task *task, size_t size)
49 {
50         struct scsi_allocated_memory *mem;
51
52         mem = malloc(sizeof(struct scsi_allocated_memory));
53         if (mem == NULL) {
54                 printf("Failed to allocate memory to scsi task\n");
55                 return NULL;
56         }
57         bzero(mem, sizeof(struct scsi_allocated_memory));
58         mem->ptr = malloc(size);
59         if (mem->ptr == NULL) {
60                 printf("Failed to allocate memory buffer for scsi task\n");
61                 free(mem);
62                 return NULL;
63         }
64         bzero(mem->ptr, size);
65         DLIST_ADD(task->mem, mem);
66         return mem->ptr;
67 }
68
69 /*
70  * TESTUNITREADY
71  */
72 struct scsi_task *scsi_cdb_testunitready(void)
73 {
74         struct scsi_task *task;
75
76         task = malloc(sizeof(struct scsi_task));
77         if (task == NULL) {
78                 printf("Failed to allocate scsi task structure\n");
79                 return NULL;
80         }
81
82         bzero(task, sizeof(struct scsi_task));
83         task->cdb[0]   = SCSI_OPCODE_TESTUNITREADY;
84
85         task->cdb_size   = 6;
86         task->xfer_dir   = SCSI_XFER_NONE;
87         task->expxferlen = 0;
88
89         return task;
90 }
91
92
93 /*
94  * REPORTLUNS
95  */
96 struct scsi_task *scsi_reportluns_cdb(int report_type, int alloc_len)
97 {
98         struct scsi_task *task;
99
100         task = malloc(sizeof(struct scsi_task));
101         if (task == NULL) {
102                 printf("Failed to allocate scsi task structure\n");
103                 return NULL;
104         }
105
106         bzero(task, sizeof(struct scsi_task));
107         task->cdb[0]   = SCSI_OPCODE_REPORTLUNS;
108         task->cdb[2]   = report_type;
109         *(uint32_t *)&task->cdb[6] = htonl(alloc_len);
110
111         task->cdb_size = 12;
112         task->xfer_dir = SCSI_XFER_READ;
113         task->expxferlen = alloc_len;
114
115         task->params.reportluns.report_type = report_type;
116
117         return task;
118 }
119
120 /*
121  * parse the data in blob and calcualte the size of a full report luns datain structure
122  */
123 static int scsi_reportluns_datain_getfullsize(struct scsi_task *task)
124 {
125         uint32_t list_size;
126
127         list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
128         
129         return list_size;
130 }
131
132 /*
133  * unmarshall the data in blob for reportluns into a structure
134  */
135 static struct scsi_reportluns_list *scsi_reportluns_datain_unmarshall(struct scsi_task *task)
136 {
137         struct scsi_reportluns_list *list;
138         int list_size;
139         int i, num_luns;
140
141         if (task->datain.size < 4) {
142                 printf("not enough data for reportluns list length\n");
143                 return NULL;
144         }
145
146         list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
147         if (list_size < task->datain.size) {
148                 printf("not enough data to unmarshall reportluns data\n");
149                 return NULL;
150         }
151
152         num_luns = list_size / 8 - 1;
153         list = scsi_malloc(task, offsetof(struct scsi_reportluns_list, luns) + sizeof(uint16_t) * num_luns);
154         if (list == NULL) {
155                 printf("Failed to allocate reportluns structure\n");
156                 return NULL;
157         }
158
159         list->num = num_luns;
160         for (i=0; i<num_luns; i++) {
161                 list->luns[i] = htons(*(uint16_t *)&(task->datain.data[i*8+8]));
162         }
163         
164         return list;
165 }
166
167
168 /*
169  * READCAPACITY10
170  */
171 struct scsi_task *scsi_cdb_readcapacity10(int lba, int pmi)
172 {
173         struct scsi_task *task;
174
175         task = malloc(sizeof(struct scsi_task));
176         if (task == NULL) {
177                 printf("Failed to allocate scsi task structure\n");
178                 return NULL;
179         }
180
181         bzero(task, sizeof(struct scsi_task));
182         task->cdb[0]   = SCSI_OPCODE_READCAPACITY10;
183
184         *(uint32_t *)&task->cdb[2] = htonl(lba);
185
186         if (pmi) {
187                 task->cdb[8] |= 0x01;
188         }
189
190         task->cdb_size = 10;
191         task->xfer_dir = SCSI_XFER_READ;
192         task->expxferlen = 8;
193
194         task->params.readcapacity10.lba = lba;
195         task->params.readcapacity10.pmi = pmi;
196
197         return task;
198 }
199
200 /*
201  * parse the data in blob and calcualte the size of a full readcapacity10 datain structure
202  */
203 static int scsi_readcapacity10_datain_getfullsize(struct scsi_task *task
204                                                   UNUSED)
205 {
206         return 8;
207 }
208
209 /*
210  * unmarshall the data in blob for readcapacity10 into a structure
211  */
212 static struct scsi_readcapacity10 *scsi_readcapacity10_datain_unmarshall(struct scsi_task *task)
213 {
214         struct scsi_readcapacity10 *rc10;
215
216         if (task->datain.size < 8) {
217                 printf("Not enough data to unmarshall readcapacity10\n");
218                 return NULL;
219         }
220         rc10 = malloc(sizeof(struct scsi_readcapacity10));
221         if (rc10 == NULL) {
222                 printf("Failed to allocate readcapacity10 structure\n");
223                 return NULL;
224         }
225
226         rc10->lba        = htonl(*(uint32_t *)&(task->datain.data[0]));
227         rc10->block_size = htonl(*(uint32_t *)&(task->datain.data[4]));
228
229         return rc10;
230 }
231
232
233
234
235
236 /*
237  * INQUIRY
238  */
239 struct scsi_task *scsi_cdb_inquiry(int evpd, int page_code, int alloc_len)
240 {
241         struct scsi_task *task;
242
243         task = malloc(sizeof(struct scsi_task));
244         if (task == NULL) {
245                 printf("Failed to allocate scsi task structure\n");
246                 return NULL;
247         }
248
249         bzero(task, sizeof(struct scsi_task));
250         task->cdb[0]   = SCSI_OPCODE_INQUIRY;
251
252         if (evpd) {
253                 task->cdb[1] |= 0x01;
254         }
255
256         task->cdb[2] = page_code;
257
258         *(uint16_t *)&task->cdb[3] = htons(alloc_len);
259
260         task->cdb_size = 6;
261         task->xfer_dir = SCSI_XFER_READ;
262         task->expxferlen = alloc_len;
263
264         task->params.inquiry.evpd      = evpd;
265         task->params.inquiry.page_code = page_code;
266
267         return task;
268 }
269
270 /*
271  * parse the data in blob and calcualte the size of a full inquiry datain structure
272  */
273 static int scsi_inquiry_datain_getfullsize(struct scsi_task *task)
274 {
275         if (task->params.inquiry.evpd != 0) {
276                 printf("Can not handle extended inquiry yet\n");
277                 return -1;
278         }
279
280         /* standard inquiry*/
281         return task->datain.data[4] + 3;
282 }
283
284 /*
285  * unmarshall the data in blob for inquiry into a structure
286  */
287 static void *scsi_inquiry_datain_unmarshall(struct scsi_task *task)
288 {
289         struct scsi_inquiry_standard *inq;
290
291         if (task->params.inquiry.evpd != 0) {
292                 printf("Can not handle extended inquiry yet\n");
293                 return NULL;
294         }
295
296         /* standard inquiry */
297         inq = scsi_malloc(task, sizeof(struct scsi_inquiry_standard));
298         if (inq == NULL) {
299                 printf("Failed to allocate standard inquiry structure\n");
300                 return NULL;
301         }
302
303        inq->periperal_qualifier    = (task->datain.data[0]>>5)&0x07;
304        inq->periperal_device_type  = task->datain.data[0]&0x1f;
305        inq->rmb                    = task->datain.data[1]&0x80;
306        inq->version                = task->datain.data[2];
307        inq->normaca                = task->datain.data[3]&0x20;
308        inq->hisup                  = task->datain.data[3]&0x10;
309        inq->response_data_format   = task->datain.data[3]&0x0f;
310
311        memcpy(&inq->vendor_identification[0], &task->datain.data[8], 8);
312        memcpy(&inq->product_identification[0], &task->datain.data[16], 16);
313        memcpy(&inq->product_revision_level[0], &task->datain.data[32], 4);
314
315        return inq;
316 }
317
318 /*
319  * READ10
320  */
321 struct scsi_task *scsi_cdb_read10(int lba, int xferlen, int blocksize)
322 {
323         struct scsi_task *task;
324
325         task = malloc(sizeof(struct scsi_task));
326         if (task == NULL) {
327                 printf("Failed to allocate scsi task structure\n");
328                 return NULL;
329         }
330
331         bzero(task, sizeof(struct scsi_task));
332         task->cdb[0]   = SCSI_OPCODE_READ10;
333
334         *(uint32_t *)&task->cdb[2] = htonl(lba);
335         *(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
336
337         task->cdb_size = 10;
338         task->xfer_dir = SCSI_XFER_READ;
339         task->expxferlen = xferlen;
340
341         return task;
342 }
343
344 /*
345  * WRITE10
346  */
347 struct scsi_task *scsi_cdb_write10(int lba, int xferlen, int fua, int fuanv, int blocksize)
348 {
349         struct scsi_task *task;
350
351         task = malloc(sizeof(struct scsi_task));
352         if (task == NULL) {
353                 printf("Failed to allocate scsi task structure\n");
354                 return NULL;
355         }
356
357         bzero(task, sizeof(struct scsi_task));
358         task->cdb[0]   = SCSI_OPCODE_WRITE10;
359
360         if (fua) {
361                 task->cdb[1] |= 0x08;
362         }
363         if (fuanv) {
364                 task->cdb[1] |= 0x02;
365         }
366
367         *(uint32_t *)&task->cdb[2] = htonl(lba);
368         *(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
369
370         task->cdb_size = 10;
371         task->xfer_dir = SCSI_XFER_WRITE;
372         task->expxferlen = xferlen;
373
374         return task;
375 }
376
377
378
379 /*
380  * MODESENSE6
381  */
382 struct scsi_task *scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len)
383 {
384         struct scsi_task *task;
385
386         task = malloc(sizeof(struct scsi_task));
387         if (task == NULL) {
388                 printf("Failed to allocate scsi task structure\n");
389                 return NULL;
390         }
391
392         bzero(task, sizeof(struct scsi_task));
393         task->cdb[0]   = SCSI_OPCODE_MODESENSE6;
394
395         if (dbd) {
396                 task->cdb[1] |= 0x08;
397         }
398         task->cdb[2] = pc<<6 | page_code;
399         task->cdb[3] = sub_page_code;
400         task->cdb[4] = alloc_len;
401
402         task->cdb_size = 6;
403         task->xfer_dir = SCSI_XFER_READ;
404         task->expxferlen = alloc_len;
405
406         task->params.modesense6.dbd           = dbd;
407         task->params.modesense6.pc            = pc;
408         task->params.modesense6.page_code     = page_code;
409         task->params.modesense6.sub_page_code = sub_page_code;
410  
411         return task;
412 }
413
414 /*
415  * parse the data in blob and calcualte the size of a full report luns datain structure
416  */
417 static int scsi_modesense6_datain_getfullsize(struct scsi_task *task)
418 {
419         int len;
420
421         len = task->datain.data[0] + 1;
422
423         return len;
424 }
425
426
427
428 int scsi_datain_getfullsize(struct scsi_task *task)
429 {
430         switch (task->cdb[0]) {
431         case SCSI_OPCODE_TESTUNITREADY:
432                 return 0;
433         case SCSI_OPCODE_INQUIRY:
434                 return scsi_inquiry_datain_getfullsize(task);
435         case SCSI_OPCODE_MODESENSE6:
436                 return scsi_modesense6_datain_getfullsize(task);
437         case SCSI_OPCODE_READCAPACITY10:
438                 return scsi_readcapacity10_datain_getfullsize(task);
439 //      case SCSI_OPCODE_READ10:
440 //      case SCSI_OPCODE_WRITE10:
441         case SCSI_OPCODE_REPORTLUNS:
442                 return scsi_reportluns_datain_getfullsize(task);
443         }
444         printf("Unknown opcode:%d for datain get full size\n", task->cdb[0]);
445         return -1;
446 }
447
448 void *scsi_datain_unmarshall(struct scsi_task *task)
449 {
450         switch (task->cdb[0]) {
451         case SCSI_OPCODE_TESTUNITREADY:
452                 return NULL;
453         case SCSI_OPCODE_INQUIRY:
454                 return scsi_inquiry_datain_unmarshall(task);
455         case SCSI_OPCODE_READCAPACITY10:
456                 return scsi_readcapacity10_datain_unmarshall(task);
457 //      case SCSI_OPCODE_READ10:
458 //      case SCSI_OPCODE_WRITE10:
459         case SCSI_OPCODE_REPORTLUNS:
460                 return scsi_reportluns_datain_unmarshall(task);
461         }
462         printf("Unknown opcode:%d for datain unmarshall\n", task->cdb[0]);
463         return NULL;
464 }
465