linux IDE驱动分析之IDE总线、驱动注册(四)
<br />IDE总线、驱动注册(四)<br /><br />418行是个宏定义<br />#define OK_STAT(stat,good,bad)(((stat)&((good)|(bad)))==(good))<br />这个宏看上去有点好玩,就是说只有好的,没有坏的就认为是好….<br />对应到我们程序的上下文就是“现在的状态是准备好,而且不忙,那么就返回1”<br />420行调用i
IDE总线、驱动注册(四)
418行是个宏定义
#define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good))
这个宏看上去有点好玩,就是说只有好的,没有坏的就认为是好….
对应到我们程序的上下文就是“现在的状态是准备好,而且不忙,那么就返回1”
420行调用ide_dev_read_id(drive, cmd, id, 0),跟踪一下源码如下:
[ide_generic_init]->[ide_host_add]->[ide_host_register]-> [ide_probe_port]->[ probe_for_drives]->[do_probe]->[ ide_dev_read_id]
236 /**
237 * ide_dev_read_id - send ATA/ATAPI IDENTIFY command
238 * @drive: drive to identify
239 * @cmd: command to use
240 * @id: buffer for IDENTIFY data
241 * @irq_ctx: flag set when called from the IRQ context
242 *
243 * Sends an ATA(PI) IDENTIFY request to a drive and waits for a response.
244 *
245 * Returns: 0 device was identified
246 * 1 device timed-out (no response to identify request)
247 * 2 device aborted the command (refused to identify itself)
248 */
249
250 int ide_dev_read_id(ide_drive_t *drive, u8 cmd, u16 *id, int irq_ctx)
251 {
252 ide_hwif_t *hwif = drive->hwif;
253 struct ide_io_ports *io_ports = &hwif->io_ports;
254 const struct ide_tp_ops *tp_ops = hwif->tp_ops;
255 int use_altstatus = 0, rc;
256 unsigned long timeout;
257 u8 s = 0, a = 0;
258
259 /*
260 * Disable device IRQ. Otherwise we'll get spurious interrupts
261 * during the identify phase that the IRQ handler isn't expecting.
262 */
263 if (io_ports->ctl_addr)
264 tp_ops->write_devctl(hwif, ATA_NIEN | ATA_DEVCTL_OBS);
265
266 /* take a deep breath */
267 if (irq_ctx)
268 mdelay(50);
269 else
270 msleep(50);
271
272 if (io_ports->ctl_addr &&
273 (hwif->host_flags & IDE_HFLAG_BROKEN_ALTSTATUS) == 0) {
274 a = tp_ops->read_altstatus(hwif);
275 s = tp_ops->read_status(hwif);
276 if ((a ^ s) & ~ATA_IDX)
277 /* ancient Seagate drives, broken interfaces */
278 printk(KERN_INFO "%s: probing with STATUS(0x%02x) "
279 "instead of ALTSTATUS(0x%02x)/n",
280 drive->name, s, a);
281 else
282 /* use non-intrusive polling */
283 use_altstatus = 1;
284 }
285
286 /* set features register for atapi
287 * identify command to be sure of reply
288 */
289 if (cmd == ATA_CMD_ID_ATAPI) {
290 struct ide_taskfile tf;
291
292 memset(&tf, 0, sizeof(tf));
293 /* disable DMA & overlap */
294 tp_ops->tf_load(drive, &tf, IDE_VALID_FEATURE);
295 }
296
297 /* ask drive for ID */
298 tp_ops->exec_command(hwif, cmd);
299
300 timeout = ((cmd == ATA_CMD_ID_ATA) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
301
302 /* wait for IRQ and ATA_DRQ */
303 if (irq_ctx) {
304 rc = __ide_wait_stat(drive, ATA_DRQ, BAD_R_STAT, timeout, &s);
305 if (rc)
306 return 1;
307 } else {
308 rc = ide_busy_sleep(drive, timeout, use_altstatus);
309 if (rc)
310 return 1;
311
312 msleep(50);
313 s = tp_ops->read_status(hwif);
314 }
315
316 if (OK_STAT(s, ATA_DRQ, BAD_R_STAT)) {
317 /* drive returned ID */
318 do_identify(drive, cmd, id);
319 /* drive responded with ID */
320 rc = 0;
321 /* clear drive IRQ */
322 (void)tp_ops->read_status(hwif);
323 } else {
324 /* drive refused ID */
325 rc = 2;
326 }
327 return rc;
328 }
既然函数有详细的注释我们就先来看看注释好了,实际上他就是发送一个Identify Drive 命令控制字为0xEC。传输回来512个字节的块数据,在这里就存放在我们之前所说的drive->id字段中。另外,数据的具体组织格式在IDE SPCE有规定,这里我们参考ATA5的标准进行分析。具体的内容比较多但为了方便还是贴出来,见下表:
注释的最后提到了返回值:
0:表示成功识别
1:表示识别超时
2:表示驱动器不允许该命令
有了这些背景之后我们再来看看源码:
263-264行道理很简单这里我们在识别的过程中不使用中断,所有调用写控制寄存器命令来关闭中断,我们还是来看一下write_devctl对应的操作,代码如下:
void ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
{
if (hwif->host_flags & IDE_HFLAG_MMIO)
writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr);
else
outb(ctl, hwif->io_ports.ctl_addr);
}
代码比较简单,这里我们主要来看看控制寄存器的地址分配和控制命令的数据格式。
第二组寄存器的地址分配表
将该寄存器的SRST位设置为1,可以使硬盘驱动器处于复位状态。IEN表示
是否允许中断,其中0为允许。分别来看一下ATA_NIEN、TA_DEVCTL_OBS这两个的定义就比较清楚了。
ATA_NIEN = (1 << 1), /* disable-irq flag */
ATA_DEVCTL_OBS = (1 << 3), /* obsolete bit in devctl reg */
267-270行始终都是延时50mS,不知为何这样处理…
274行调用ad_altstatus所对应的代码如下:
58 u8 ide_read_altstatus(ide_hwif_t *hwif)
59 {
60 if (hwif->host_flags & IDE_HFLAG_MMIO)
61 return readb((void __iomem *)hwif->io_ports.ctl_addr);
62 else
63 return inb(hwif->io_ports.ctl_addr);
64 }
这里读的是控制寄存器地址,实际上从上面那张第二组寄存器的地址分配表
中我们不难得到实际读到的应该是交换状态寄存器(只读寄存器)的值。
272-248 行如果这两个寄存器读的数据不一致的话就以STATUS寄存器中的值为准,不再读取交换状态寄存器的值。
289-295行ATAPI接口的和我们无关,略过….
298行就是真正执行命令了,调用exec_command实际上就是向命令寄存器写数据。
300行设置一个超时的时间,对于ATA接口定义为30s。
303-314行就是等待设备准备好了,这里根据中断上下文的不同,共提供了两种方法,这里我们选择308行。即调用ide_busy_sleep,下面看下源码:
[ide_generic_init]->[ide_host_add]->[ide_host_register]-> [ide_probe_port]->[ probe_for_drives]->[do_probe]->[ ide_dev_read_id]->[ ide_busy_sleep]
330 int ide_busy_sleep(ide_drive_t *drive, unsigned long timeout, int altstatus)
331 {
332 ide_hwif_t *hwif = drive->hwif;
333 u8 stat;
334
335 timeout += jiffies;
336
337 do {
338 msleep(50); /* give drive a breather */
339 stat = altstatus ? hwif->tp_ops->read_altstatus(hwif)
340 : hwif->tp_ops->read_status(hwif);
341 if ((stat & ATA_BUSY) == 0)
342 return 0;
343 } while (time_before(jiffies, timeout));
344
345 printk(KERN_ERR "%s: timeout in %s/n", drive->name, __func__);
346
347 return 1; /* drive timed-out */
348 }
这段代码比较简单就不在详细述说,实际上就是每隔50ms查询一次设备状态,要是状态不为忙就返回0,否则30s过去了一直为忙状态。那对不起您超时了,直接返回1。
312-313行很明显就是稍微延时一下,以满足设备操作时序上的要求,毕竟不是所有的设备都能做到反应这么迅速。前面已经不忙了,那么这里就再读出设备的状态来,一边后面做进一步的判断。
316行前面已经说过了要是设备的状态说“我不忙了前面的操作也没发生错误(#define BAD_R_STAT (ATA_BUSY | ATA_ERR)),而且准备好了
( ATA_DRQ= (1 << 3), /* data request i/o */)”
那么好,我们就要开始读这些identify数据了。在说do_identify之前稍稍打断一下,说点关于硬盘的闲话。我们知道硬盘内部的读写是个机械操作的过程,和我们这些CPU的指令操作的速度肯定是没办法比,为此呢,对硬盘的读写之前我们始终会去读它当前的状态信息,当然最重要的两个就是busy和ready。但是什么时候busy,什么时候又是ready呢?一般来讲比如当我们读硬盘扇区的时候,先回告诉硬盘要读的扇区的位置,无论是LBA方式还是CHS方式都必须设置硬盘中的几个地址寄存器(暂时这么叫)。这个地址写到硬盘里面去了以后,硬盘里面的控制器就知道了,哦你原来要找的使这个地址啊。然后控制器就调度硬盘的盘片转啊转啊,终于转到这个地址了。然后就高兴地告诉主机,主机、主机我准备好了,告诉俺是让俺读数据呢还是写呢?当然这是相当理想的情况,要是哪个缺德的家伙写了个硬盘根本寻址不到的地址,或者硬盘那个扇区坏了,那可不好硬盘就好发脾气了,你忽悠俺,当然发脾气也坏不到哪去,顶多就是不设置准备标志位,然后给出错误状态信息供主机查询。好了ready已经说了,主机知道控制器准备好了,当然也很高兴,就又开始下达任务了,比如给我去读10个扇区的数据回来。这个时候硬盘控制器一听高兴的啊,又有事干了,并且设置忙标志位,然后开始一个字节字节的从磁盘盘片上搬到硬盘的cash中。这个过程结束以后硬盘的机械动作也就完成了,然后忙标志位就被清除了。主机一看不忙了,就知道硬盘的事情又干完了,剩下的事就是主机和cash了,当然这个速度就是很快的了,最后就一个字节一个字节的往主机里面读,完成了数据的读取。当然这只是针对PIO访问模式,DMA方式又另当别论了……
好像有点扯远了,回到主题上面,接下来该是318行的 do_identify(drive, cmd, id);还是来先看源代码怎么说。
[ide_generic_init]->[ide_host_add]->[ide_host_register]-> [ide_probe_port]->[ probe_for_drives]->[do_probe]->[ ide_dev_read_id]->[do_identify]
180 /**
181 * do_identify - identify a drive
182 * @drive: drive to identify
183 * @cmd: command used
184 * @id: buffer for IDENTIFY data
185 *
186 * Called when we have issued a drive identify command to
187 * read and parse the results. This function is run with
188 * interrupts disabled.
189 */
190
191 static void do_identify(ide_drive_t *drive, u8 cmd, u16 *id)
192 {
193 ide_hwif_t *hwif = drive->hwif;
194 char *m = (char *)&id[ATA_ID_PROD];
195 unsigned long flags;
196 int bswap = 1;
197
198 /* local CPU only; some systems need this */
199 local_irq_save(flags);
200 /* read 512 bytes of id info */
201 hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE);
202 local_irq_restore(flags);
203
204 drive->dev_flags |= IDE_DFLAG_ID_READ;
205 #ifdef DEBUG
206 printk(KERN_INFO "%s: dumping identify data/n", drive->name);
207 ide_dump_identify((u8 *)id);
208 #endif
209 ide_fix_driveid(id);
210
211 /*
212 * ATA_CMD_ID_ATA returns little-endian info,
213 * ATA_CMD_ID_ATAPI *usually* returns little-endian info.
214 */
215 if (cmd == ATA_CMD_ID_ATAPI) {
216 if ((m[0] == 'N' && m[1] == 'E') || /* NEC */
217 (m[0] == 'F' && m[1] == 'X') || /* Mitsumi */
218 (m[0] == 'P' && m[1] == 'i')) /* Pioneer */
219 /* Vertos drives may still be weird */
220 bswap ^= 1;
221 }
222
223 ide_fixstring(m, ATA_ID_PROD_LEN, bswap);
224 ide_fixstring((char*)&id[ATA_ID_FW_REV],ATA_ID_FW_REV_LEN, bswap);
225 ide_fixstring((char *)&id[ATA_ID_SERNO], ATA_ID_SERNO_LEN, bswap);
226
227 /* we depend on this a lot! */
228 m[ATA_ID_PROD_LEN - 1] = '/0';
229
230 if (strstr(m, "E X A B Y T E N E S T"))
231 drive->dev_flags &= ~IDE_DFLAG_PRESENT;
232 else
233 drive->dev_flags |= IDE_DFLAG_PRESENT;
234 }
194行这里是个指向字符串的指针,其中ATA_ID_PROD= 27,我们参考前面所给的那个id扇区表,来看一下。
实际上是驱动器的ASCII描述。
199-202行这里为了保证读取的完整性,使用了 local_irq_save(flags);进行保护。
201行就会调用 hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE);进行数据输入了 ,相应的源代码如下:
[ide_io_std.c]
156 /*
157 * This is used for most PIO data transfers *from* the IDE interface
158 *
159 * These routines will round up any request for an odd number of bytes,
160 * so if an odd len is specified, be sure that there's at least one
161 * extra byte allocated for the buffer.
162 */
163 void ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf,
164 unsigned int len)
165 {
166 ide_hwif_t *hwif = drive->hwif;
167 struct ide_io_ports *io_ports = &hwif->io_ports;
168 unsigned long data_addr = io_ports->data_addr;
169 unsigned int words = (len + 1) >> 1;
170 u8 io_32bit = drive->io_32bit;
171 u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
172
173 if (io_32bit) {
174 unsigned long uninitialized_var(flags);
175
176 if ((io_32bit & 2) && !mmio) {
177 local_irq_save(flags);
178 ata_vlb_sync(io_ports->nsect_addr);
179 }
180
181 words >>= 1;
182 if (mmio)
183 __ide_mm_insl((void __iomem *)data_addr, buf, words);
184 else
185 insl(data_addr, buf, words);
186
187 if ((io_32bit & 2) && !mmio)
188 local_irq_restore(flags);
189
190 if (((len + 1) & 3) < 2)
191 return;
192
193 buf += len & ~3;
194 words = 1;
195 }
196
197 if (mmio)
198 __ide_mm_insw((void __iomem *)data_addr, buf, words);
199 else
200 insw(data_addr, buf, words);
201 }
169行这个函数当中的数据是以16bit的形式访问的,所有当访问数据长度是奇数字节的时候,我们就多读一个字节,这是很容易理解的。
170行如果定义了32bit的形式访问的话,后面就采用相应的方式来处理数据。
171行IO内存和IO端口相关的前面说过不再重复。
173-195行是32bit相关的,与我们无关跳过。
197-200行我们只关心198行,原因大家都知道。重点来看看__ide_mm_insw,源代码如下:
[ide_iops.h]
8 static __inline__ void __ide_mm_insw(void __iomem *port, void *addr, u32 count)
9 {
10 while (count--) {
11 *(u16 *)addr = readw(port);
12 addr += 2;
13 }
14 }
与IDE接口进行的数据交换全是通过数据寄存器进行的,这段代码就很简单了,就不详细说了。
好了,ide_input_data返回后,我们继续将目光投向do_identify。
204行一直走过来我们已经与drive->dev_flags碰了不少面了,但是每次都没打过招呼。那么我们现在就来认识一下他,早在do_probe,我们就见到过这样一个定义
u8 present = !!(drive->dev_flags & IDE_DFLAG_PRESENT), stat;
IDE_DFLAG_PRESENT实际上就是暗示设备是否在ide总线上,这里又出现一个IDE_DFLAG_ID_READ标示,就表示这个驱动器的id信息我们已经读取出来了。所以我们很容就能想到,dev-flags实际上就是设备当前状态的一个标示。随着代码的深入我们有预感还会和他相遇…..
205-208行看到#ifdef DEBUG我们就皆大欢喜,不用管它了…
209行 ide_fix_driveid(id)看意思好像是要修改drive id的数据,那我们就进去看他怎么修改吧。
[ide_iops.c]
48 void ide_fix_driveid(u16 *id)
49 {
50 #ifndef __LITTLE_ENDIAN
51 # ifdef __BIG_ENDIAN
52 int i;
53
54 for (i = 0; i < 256; i++)
55 id[i] = __le16_to_cpu(id[i]);
56 # else
57 # error "Please fix <asm/byteorder.h>"
58 # endif
59 #endif
60 }
50-51行一眼就看到了我们所熟悉的大小端模式了,毕竟我们的linux要支持太多的体系结构,不同的cpu数据在内存中存放的格式可能不一样。但是我们硬盘以小端形式存放的,就是高字节存放在高地址空间,低字节存放在低位地址空间,这里强制类型转换为大端格式。
215-221行是ATAPI相关的,这里跳过…
223-225行又是修改什么string,那我们就干脆一起看了,源码如下:
[ide_generic_init]->[ide_host_add]->[ide_host_register]-> [ide_probe_port]->[ probe_for_drives]->[do_probe]->[ ide_dev_read_id]->[do_identify]->
[ide_fixstring]
62 /*
63 * ide_fixstring() cleans up and (optionally) byte-swaps a text string,
64 * removing leading/trailing blanks and compressing internal blanks.
65 * It is primarily used to tidy up the model name/number fields as
66 * returned by the ATA_CMD_ID_ATA[PI] commands.
67 */
68
69 void ide_fixstring(u8 *s, const int bytecount, const int byteswap)
70 {
71 u8 *p, *end = &s[bytecount & ~1]; /* bytecount must be even */
72
73 if (byteswap) {
74 /* convert from big-endian to host byte order */
75 for (p = s ; p != end ; p += 2)
76 be16_to_cpus((u16 *) p);
77 }
78
79 /* strip leading blanks */
80 p = s;
81 while (s != end && *s == ' ')
82 ++s;
83 /* compress internal blanks and strip trailing blanks */
84 while (s != end && *s) {
85 if (*s++ != ' ' || (s != end && *s && *s != ' '))
86 *p++ = *(s-1);
87 }
88 /* wipe out trailing garbage */
89 while (p != end)
90 *p++ = '/0';
91 }
73-77行前面已经提到这个问题了,又是端格式的问题。就是将大端格式转换为主机格式。
79-90行这就比较简单了,实际上作的工作就是将s所对应的字符串中前面的空格字符删除,并将多余的位置置为空,为了更形象举个例子吧。比如s=“ lin ux ”,经过整理后就成了s=“linux”,多余的位就全部设置为0了,就这么简单。
继续回到do_identify中,来看ide_fixstring的调用关系,
223行相关的前面定义m的时候已经说过;
224行定义ATA_ID_FW_REV= 23,对应的是8个字符的固件版本号;
225行定义 ATA_ID_SERNO= 10,对应20个字节的Serial number;
230行strstr,这是一个字符串的操作函数。这里给出函数原型和相应注释。
原型:extern char *strstr(char *haystack, char *needle);
用法:#include <string.h>
功能:从字符串haystack中寻找needle第一次出现的位置(不比较结束符NULL)。
说明:返回指向第一次出现needle位置的指针,如果没找到则返回NULL。
这里就是看读到的product id是否有"E X A B Y T E N E S T",如果包含那么就利用do_identify() marks EXABYTENEST device as non-present and frees drive->id。
当do_identify默默填满drive->id,并做相应的处理后,也就是他该返回的时候。依依不舍的回到ide_dev_read_id,这时候发现他也走到了生命的尽头,一切都结束了,是时候回到do_probe了…..
421-423行这两句有点像大学的考试,第一次挂了,第二次再来一次。再不行那就不能理解了,直接批上无可救药。
425行还是读一下设备的状态。
427行这句话就让人费解了,什么玩意,说自己准备好了,然后还打上个忙的旗号,那肯定是疯了,直接返回得了。
430-439行我们再说ide_dev_read_id的时候已经说到了,只有他返回0才是正常的,1表示超时。那么设备肯定是有问题了,当然这肯定不是我们希望的,这段代码就是关于超时了的,并且是ATAPI接口的,略过。
442行再次查询设备状态,烦不烦啦,但是没有办法,前面的操作中我们禁止了IDE的中断,但是中断标志还是存在的,就像我们很多MCU一样,中断被屏蔽了但是相应的标志位还是会置位的。当重新使能中断的时候这些之前的标志位还是应该清除掉的,否则再次会引发中断。对ide接口的状态寄存器读,会清零中断标志。所以为了确保清除irq我们不得不这么做啊…..
451-457行重新选择主驱动器,并清除IRQ标志。
一路走来,感觉和do_probe一起走了好久好久,现在他总算快要离我们而去了,还是回忆一下和他走过的快乐时光,do_probe做的最有意义的一件事情就是调用ide_dev_read_id完成了硬盘identify数据的读取。离开do_probe,回到probe_for_drives中来的时候发现我们还只是刚刚开始…..
更多推荐
所有评论(0)