linux IDE驱动分析之IDE总线、驱动注册(四)

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_NIENTA_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-295ATAPI接口的和我们无关,略过.

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的指令操作的速度肯定是没办法比,为此呢,对硬盘的读写之前我们始终会去读它当前的状态信息,当然最重要的两个就是busyready。但是什么时候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的形式访问的话,后面就采用相应的方式来处理数据。

171IO内存和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

230strstr,这是一个字符串的操作函数。这里给出函数原型和相应注释。

原型: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中来的时候发现我们还只是刚刚开始..

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值