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