1 /*
2  * This file is part of MPlayer.
3  *
4  * MPlayer 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 2 of the License, or
7  * (at your option) any later version.
8  *
9  * MPlayer 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 along
15  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #ifndef MPLAYER_VCD_READ_FBSD_H
20 #define MPLAYER_VCD_READ_FBSD_H
21 
22 #define _XOPEN_SOURCE 500
23 
24 #include <sys/types.h>
25 #include <inttypes.h>
26 #include <unistd.h>
27 #include "libavutil/intreadwrite.h"
28 #include <sys/cdio.h>
29 #include <sys/ioctl.h>
30 #if defined(__NetBSD__) || defined(__OpenBSD__)
31 #define VCD_NETBSD 1
32 #endif
33 #ifdef VCD_NETBSD
34 #include <sys/scsiio.h>
35 #define TOCADDR(te) ((te).data->addr)
36 #define READ_TOC CDIOREADTOCENTRYS
37 #else
38 #include <sys/cdrio.h>
39 #define TOCADDR(te) ((te).entry.addr)
40 #define READ_TOC CDIOREADTOCENTRY
41 #endif
42 #include "mp_msg.h"
43 
44 //=================== VideoCD ==========================
45 #define	CDROM_LEADOUT	0xAA
46 
47 typedef struct {
48 	uint8_t sync            [12];
49 	uint8_t header          [4];
50 	uint8_t subheader       [8];
51 	uint8_t data            [2324];
52 	uint8_t spare           [4];
53 } cdsector_t;
54 
55 #ifdef VCD_NETBSD
56 typedef struct ioc_read_toc_entry vcd_tocentry;
57 #else
58 typedef struct ioc_read_toc_single_entry vcd_tocentry;
59 #endif
60 
61 typedef struct mp_vcd_priv_st {
62   int fd;
63   vcd_tocentry entry;
64 #ifdef VCD_NETBSD
65   struct cd_toc_entry entry_data;
66 #else
67   cdsector_t buf;
68 #endif
69   struct ioc_toc_header tochdr;
70   unsigned int track;
71 } mp_vcd_priv_t;
72 
73 static inline void
vcd_set_msf(mp_vcd_priv_t * vcd,unsigned int sect)74 vcd_set_msf(mp_vcd_priv_t* vcd, unsigned int sect)
75 {
76 #ifdef VCD_NETBSD
77   vcd->entry.data = &vcd->entry_data;
78 #endif
79   sect += 150;
80   TOCADDR(vcd->entry).msf.frame = sect % 75;
81   sect = sect / 75;
82   TOCADDR(vcd->entry).msf.second = sect % 60;
83   sect = sect / 60;
84   TOCADDR(vcd->entry).msf.minute = sect;
85 }
86 
87 static inline void
vcd_inc_msf(mp_vcd_priv_t * vcd)88 vcd_inc_msf(mp_vcd_priv_t* vcd)
89 {
90 #ifdef VCD_NETBSD
91   vcd->entry.data = &vcd->entry_data;
92 #endif
93   TOCADDR(vcd->entry).msf.frame++;
94   if (TOCADDR(vcd->entry).msf.frame==75){
95     TOCADDR(vcd->entry).msf.frame=0;
96     TOCADDR(vcd->entry).msf.second++;
97     if (TOCADDR(vcd->entry).msf.second==60){
98       TOCADDR(vcd->entry).msf.second=0;
99       TOCADDR(vcd->entry).msf.minute++;
100     }
101   }
102 }
103 
104 static inline unsigned int
vcd_get_msf(mp_vcd_priv_t * vcd)105 vcd_get_msf(mp_vcd_priv_t* vcd)
106 {
107 #ifdef VCD_NETBSD
108   vcd->entry.data = &vcd->entry_data;
109 #endif
110   return TOCADDR(vcd->entry).msf.frame +
111         (TOCADDR(vcd->entry).msf.second +
112          TOCADDR(vcd->entry).msf.minute * 60) * 75 - 150;
113 }
114 
115 /**
116  * \brief read a TOC entry
117  * \param fd device to read from
118  * \param dst buffer to read data into
119  * \param nr track number to read info for
120  * \return 1 on success, 0 on failure
121  */
122 static int
read_toc_entry(mp_vcd_priv_t * vcd,int nr)123 read_toc_entry(mp_vcd_priv_t *vcd, int nr)
124 {
125   vcd->entry.address_format = CD_MSF_FORMAT;
126 #ifdef VCD_NETBSD
127   vcd->entry.data_len = sizeof(struct cd_toc_entry);
128   vcd->entry.data = &vcd->entry_data;
129   vcd->entry.starting_track = nr;
130 #else
131   vcd->entry.track = nr;
132 #endif
133   if (ioctl(vcd->fd, READ_TOC, &vcd->entry) == -1) {
134     mp_msg(MSGT_OPEN,MSGL_ERR,"read CDROM toc entry: %s\n",strerror(errno));
135     return 0;
136   }
137   return 1;
138 }
139 
140 static int
vcd_seek_to_track(mp_vcd_priv_t * vcd,int track)141 vcd_seek_to_track(mp_vcd_priv_t* vcd, int track)
142 {
143   if (!read_toc_entry(vcd, track))
144     return -1;
145   return VCD_SECTOR_DATA * vcd_get_msf(vcd);
146 }
147 
148 static int
vcd_get_track_end(mp_vcd_priv_t * vcd,int track)149 vcd_get_track_end(mp_vcd_priv_t* vcd, int track)
150 {
151   if (!read_toc_entry(vcd,
152           track < vcd->tochdr.ending_track ? track + 1 : CDROM_LEADOUT))
153     return -1;
154   return VCD_SECTOR_DATA * vcd_get_msf(vcd);
155 }
156 
157 static mp_vcd_priv_t*
vcd_read_toc(int fd)158 vcd_read_toc(int fd)
159 {
160   struct ioc_toc_header tochdr;
161   mp_vcd_priv_t* vcd;
162   int i, last_startsect;
163   if (ioctl(fd, CDIOREADTOCHEADER, &tochdr) == -1) {
164     mp_msg(MSGT_OPEN,MSGL_ERR,"read CDROM toc header: %s\n",strerror(errno));
165     return NULL;
166   }
167   mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VCD_START_TRACK=%d\n", tochdr.starting_track);
168   mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VCD_END_TRACK=%d\n", tochdr.ending_track);
169   vcd = malloc(sizeof(mp_vcd_priv_t));
170   vcd->fd = fd;
171   vcd->tochdr = tochdr;
172   for (i = tochdr.starting_track; i <= tochdr.ending_track + 1; i++) {
173     if (!read_toc_entry(vcd,
174           i <= tochdr.ending_track ? i : CDROM_LEADOUT)) {
175       free(vcd);
176       return NULL;
177     }
178 
179     if (i <= tochdr.ending_track)
180     mp_msg(MSGT_OPEN,MSGL_INFO,"track %02d:  adr=%d  ctrl=%d  format=%d  %02d:%02d:%02d\n",
181 #ifdef VCD_NETBSD
182           (int)vcd->entry.starting_track,
183           (int)vcd->entry.data->addr_type,
184           (int)vcd->entry.data->control,
185 #else
186           (int)vcd->entry.track,
187           (int)vcd->entry.entry.addr_type,
188           (int)vcd->entry.entry.control,
189 #endif
190           (int)vcd->entry.address_format,
191           (int)TOCADDR(vcd->entry).msf.minute,
192           (int)TOCADDR(vcd->entry).msf.second,
193           (int)TOCADDR(vcd->entry).msf.frame
194       );
195 
196     if (mp_msg_test(MSGT_IDENTIFY, MSGL_INFO))
197     {
198       int startsect = vcd_get_msf(vcd);
199       if (i > tochdr.starting_track)
200       {
201         // convert duraion to MSF
202         vcd_set_msf(vcd, startsect - last_startsect);
203         mp_msg(MSGT_IDENTIFY, MSGL_INFO,
204                "ID_VCD_TRACK_%d_MSF=%02d:%02d:%02d\n",
205                i - 1,
206                TOCADDR(vcd->entry).msf.minute,
207                TOCADDR(vcd->entry).msf.second,
208                TOCADDR(vcd->entry).msf.frame);
209       }
210       last_startsect = startsect;
211     }
212   }
213   return vcd;
214 }
215 
vcd_end_track(mp_vcd_priv_t * vcd)216 static int vcd_end_track(mp_vcd_priv_t* vcd)
217 {
218   return vcd->tochdr.ending_track;
219 }
220 
221 static int
vcd_read(mp_vcd_priv_t * vcd,char * mem)222 vcd_read(mp_vcd_priv_t* vcd, char *mem)
223 {
224 #ifdef VCD_NETBSD
225   struct scsireq  sc;
226   int             lba = vcd_get_msf(vcd);
227   int             blocks;
228   int             rc;
229 
230   blocks = 1;
231 
232   memset(&sc, 0, sizeof(sc));
233   sc.cmd[0] = 0xBE;
234   sc.cmd[1] = 5 << 2; // mode2/form2
235   AV_WB32(&sc.cmd[2], lba);
236   AV_WB24(&sc.cmd[6], blocks);
237   sc.cmd[9] = 1 << 4; // user data only
238   sc.cmd[10] = 0;     // no subchannel
239   sc.cmdlen = 12;
240   sc.databuf = (caddr_t) mem;
241   sc.datalen = VCD_SECTOR_DATA;
242   sc.senselen = sizeof(sc.sense);
243   sc.flags = SCCMD_READ;
244   sc.timeout = 10000;
245   rc = ioctl(vcd->fd, SCIOCCOMMAND, &sc);
246   if (rc == -1) {
247     mp_msg(MSGT_STREAM,MSGL_ERR,"SCIOCCOMMAND: %s\n",strerror(errno));
248     return -1;
249   }
250   if (sc.retsts || sc.error) {
251     mp_msg(MSGT_STREAM,MSGL_ERR,"scsi command failed: status %d error %d\n",
252 	   sc.retsts,sc.error);
253     return -1;
254   }
255 #else
256   if (pread(vcd->fd,&vcd->buf,VCD_SECTOR_SIZE,vcd_get_msf(vcd)*VCD_SECTOR_SIZE)
257      != VCD_SECTOR_SIZE) return 0;  // EOF?
258 
259   memcpy(mem,vcd->buf.data,VCD_SECTOR_DATA);
260 #endif
261   vcd_inc_msf(vcd);
262   return VCD_SECTOR_DATA;
263 }
264 
265 #endif /* MPLAYER_VCD_READ_FBSD_H */
266