Initial revision
[opensuse:hwinfo.git] / src / hd / scsi.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/ioctl.h>
9 #include <scsi/scsi.h>
10 #include <linux/hdreg.h>               
11 #include "scsi_ioctl.h"         // is missing(?!) on SuSE systems; taken from Debian
12
13 #include "hd.h"
14 #include "hd_int.h"
15 #include "scsi.h"
16
17 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
18  * scsi info
19  *
20  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
21  */
22
23 static void dump_scsi_data(hd_data_t *hd_data);
24
25 void hd_scan_scsi(hd_data_t *hd_data)
26 {
27
28   hd_t *hd;
29 //  unsigned char scsi_cmd_buf[256];
30   unsigned u0, u1, u2;
31   int fd, host_line, prog_cnt = 0;
32   char scsi_vend[20], scsi_model[40], scsi_rev[20], scsi_type[30];
33   char *s0, *s1, *s2;
34   unsigned sd_cnt = 0, sr_cnt = 0, st_cnt = 0, sg_cnt = 0;
35   char sd_dev[] = "/dev/sd";
36   char sr_dev[] = "/dev/sr";            // or scd?
37   char st_dev[] = "/dev/st";
38   char sg_dev[] = "/dev/sg";
39   struct hd_geometry geo;
40   str_list_t *sl;
41   hd_res_t *res;
42
43   if(!(hd_data->probe & (1 << pr_scsi))) return;
44
45   hd_data->module = mod_scsi;
46
47   /* some clean-up */
48   remove_hd_entries(hd_data);
49   hd_data->scsi = NULL;
50
51   PROGRESS(1, 0, "read info");
52
53   if(!(hd_data->scsi = read_file(PROC_SCSI_SCSI, 1, 0))) return;
54   if((hd_data->debug & HD_DEB_SCSI)) dump_scsi_data(hd_data);
55
56   PROGRESS(2, 0, "build list");  
57
58   host_line = 0;
59   for(sl = hd_data->scsi; sl; sl = sl->next) {
60     if(sscanf(sl->str, "Host: %*s Channel: %u Id: %u Lun: %u", &u0, &u1, &u2) == 3) {
61       host_line = 1;
62       *scsi_vend = *scsi_model = *scsi_rev = *scsi_type = 0;
63       continue;
64     }
65
66     if(sscanf(sl->str, " Type: %29s", scsi_type) == 1) {
67       if(host_line) {
68         hd = add_hd_entry(hd_data, __LINE__, 0);
69         hd->base_class = bc_storage_device;
70         hd->bus = bus_scsi;
71         hd->slot = (u0 << 8) + (u1 & 0xff);
72         hd->func = u2;
73
74         /*
75          * cf.
76          *   drivers/scsi/scsi.c:scsi_device_types
77          *   include/scsi/scsi.h:TYPE_...
78          *   drivers/scsi/sd.c:sd_attach()
79          *   drivers/scsi/sr.c:sr_attach()
80          */
81
82         // fix: ##### sdaa, sdab, etc. for >26
83         // TODO: sr.c:get_capabilities
84         // ##### strstr() or strcmp()???
85
86         if(!strcmp(scsi_type, "Direct-Access")) {
87           hd->sub_class = sc_sdev_disk;
88           str_printf(&hd->unix_dev_name, 0, "%s%c", sd_dev, sd_cnt + 'a');
89           sd_cnt++;
90         }
91         else if(!strstr(sl->str, "Optical Device")) {
92           hd->sub_class = sc_sdev_disk;
93           str_printf(&hd->unix_dev_name, 0, "%s%c", sd_dev, sd_cnt + 'a');
94           sd_cnt++;
95         }
96         else if(strstr(sl->str, "CD-ROM")) {
97           hd->sub_class = sc_sdev_cdrom;
98           str_printf(&hd->unix_dev_name, 0, "%s%u", sr_dev, sr_cnt);
99           sr_cnt++;
100         }
101         else if(strstr(sl->str, "WORM")) {
102           hd->sub_class = sc_sdev_cdrom;
103           str_printf(&hd->unix_dev_name, 0, "%s%u", sr_dev, sr_cnt);
104           sr_cnt++;
105         }
106         else if(strstr(sl->str, "Sequential-Access")) { // ##### is that so???
107           hd->sub_class = sc_sdev_tape;
108           str_printf(&hd->unix_dev_name, 0, "%s%u", st_dev, st_cnt);
109           st_cnt++;
110         }
111         else {
112           hd->sub_class = sc_sdev_other;
113           str_printf(&hd->unix_dev_name, 0, "%s%u", sg_dev, sg_cnt);
114         }
115
116         sg_cnt++;
117
118         if(*scsi_vend) hd->vend_name = canon_str(scsi_vend, strlen(scsi_vend));
119         if(*scsi_model) hd->dev_name = canon_str(scsi_model, strlen(scsi_model));
120         if(*scsi_rev) hd->rev_name = canon_str(scsi_rev, strlen(scsi_rev));
121
122         PROGRESS(2, ++prog_cnt, "get geo");  
123
124         fd = -1;
125         if(
126           hd->sub_class == sc_sdev_disk &&
127           hd->unix_dev_name &&
128           (fd = open(hd->unix_dev_name, O_RDONLY | O_NONBLOCK)) >= 0 &&
129           !ioctl(fd, HDIO_GETGEO, &geo)
130         ) {
131           res = add_res_entry(&hd->res, new_mem(sizeof *res));
132           res->disk_geo.type = res_disk_geo;
133           res->disk_geo.cyls = geo.cylinders;
134           res->disk_geo.heads = geo.heads;
135           res->disk_geo.sectors = geo.sectors;
136           res->disk_geo.logical = 1;
137
138           res = add_res_entry(&hd->res, new_mem(sizeof *res));
139           res->size.type = res_size;
140           res->size.unit = size_unit_sectors;
141           res->size.val1 = geo.cylinders * geo.heads * geo.sectors;
142           res->size.val2 = 512;
143
144 #if 0
145           memset(scsi_cmd_buf, 0, sizeof scsi_cmd_buf);
146           scsi_cmd_buf[8 + 0] = INQUIRY;        // command
147           scsi_cmd_buf[8 + 4] = 0x60;   // INQUIRY_REPLY_LEN
148           if(
149             !ioctl(fd, SCSI_IOCTL_SEND_COMMAND, scsi_cmd_buf)
150           ) {
151             fprintf(stderr, "0x%02x\n", scsi_cmd_buf[8]);
152           }
153 #endif
154
155           if(fd >= 0) close(fd);
156         }
157
158         host_line = 0;
159       }
160     }
161
162     if(
163       (s0 = strstr(sl->str, "Vendor:")) &&
164       (s1 = strstr(sl->str, "Model:")) &&
165       (s2 = strstr(sl->str, "Rev:"))
166     ) {
167       *s1 = *s2 = 0;
168       s0 += sizeof "Vendor:" - 1;
169       s1 += sizeof "Model:" - 1;
170       s2 += sizeof "Rev:" - 1;
171       strncpy(scsi_vend, s0, sizeof scsi_vend - 1);
172       strncpy(scsi_model, s1, sizeof scsi_model - 1);
173       strncpy(scsi_rev, s2, sizeof scsi_rev - 1);
174       scsi_vend[sizeof scsi_vend - 1] =
175       scsi_model[sizeof scsi_model - 1] =
176       scsi_rev[sizeof scsi_rev - 1] = 0;
177     }
178   }
179 }
180
181 /*
182  * Add some scsi data to the global log.
183  */
184 void dump_scsi_data(hd_data_t *hd_data)
185 {
186   str_list_t *sl;
187
188   ADD2LOG("----- /proc/scsi/scsi -----\n");
189   for(sl = hd_data->scsi; sl; sl = sl->next) {
190     ADD2LOG("  %s", sl->str);
191   }
192   ADD2LOG("----- /proc/scsi/scsi end -----\n");
193 }